commit aa1fb1d6f5e10f1c5611596fcea28df9b58f5f5a Author: wenyongda Date: Fri Mar 27 12:22:43 2026 +0800 init: 初始化 XiaowenBlog 博客项目,添加配置文件及技术文章 diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..93385d9 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +version: 2 +updates: +- package-ecosystem: npm + directory: "/" + schedule: + interval: daily + open-pull-requests-limit: 20 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..63f307b --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +Thumbs.db +db.json +*.log +node_modules/ +public/ +.deploy*/ +_multiconfig.yml \ No newline at end of file diff --git a/_config.butterfly.yml b/_config.butterfly.yml new file mode 100644 index 0000000..d8fb7f9 --- /dev/null +++ b/_config.butterfly.yml @@ -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: '' diff --git a/_config.yml b/_config.yml new file mode 100644 index 0000000..136b747 --- /dev/null +++ b/_config.yml @@ -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: '' diff --git a/package.json b/package.json new file mode 100644 index 0000000..3e1c289 --- /dev/null +++ b/package.json @@ -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" + } +} \ No newline at end of file diff --git a/scaffolds/draft.md b/scaffolds/draft.md new file mode 100644 index 0000000..498e95b --- /dev/null +++ b/scaffolds/draft.md @@ -0,0 +1,4 @@ +--- +title: {{ title }} +tags: +--- diff --git a/scaffolds/page.md b/scaffolds/page.md new file mode 100644 index 0000000..f01ba3c --- /dev/null +++ b/scaffolds/page.md @@ -0,0 +1,4 @@ +--- +title: {{ title }} +date: {{ date }} +--- diff --git a/scaffolds/post.md b/scaffolds/post.md new file mode 100644 index 0000000..1f9b9a4 --- /dev/null +++ b/scaffolds/post.md @@ -0,0 +1,5 @@ +--- +title: {{ title }} +date: {{ date }} +tags: +--- diff --git a/source/_posts/ASP.NET Core.md b/source/_posts/ASP.NET Core.md new file mode 100644 index 0000000..85ed9ef --- /dev/null +++ b/source/_posts/ASP.NET Core.md @@ -0,0 +1,1971 @@ +--- +title: ASP.NET Core +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 +--- +# IDE智能提示优化 + +## .Net6 的汉化 + +### 本地化xml生成工具 + +工具以`dotnet cli`发布,使用`dotnet tool`进行安装 + +```shell +dotnet tool install -g islocalizer +``` + +`.net6`的汉化包已经有现成的了,可以直接进行安装 + +```shell +islocalizer install auto -m net6.0 -l zh-cn +``` + +工具会自动从`github`下载对应的包进行安装(可能需要访问加速)。 +也可以通过`-cc`参数指定内容对照类型 + +- `OriginFirst`: 原始内容在前 +- `LocaleFirst`: 本地化内容在前 +- `None`: 没有对照 + +```shell +islocalizer install auto -m net6.0 -l zh-cn -cc OriginFirst +``` + +自定义生成 + +如下示例生成`.net6`的原始内容在前的`zh-cn`本地化包,并使用 `---------` 分隔原文和本地化内容,生成完成后的`包路径`会输出到控制台。 + +可以通过 `islocalizer build -h` 查看更多的构建参数信息。 + +首次构建过程可能非常缓慢(需要爬取所有的页面),相关文件会被缓存(单zh-cn内容大小约3.5G),再次构建时会比较快; + +安装 + +```shell +islocalizer install {包路径} +``` + +`包路径`为build命令完成后输出的路径。 + +可以通过 `islocalizer -h` 查看更多的命令帮助。 + +# Web API 项目初始化搭建 + +首先打开Visual Studio 2022,然后选择创建新项目 + +之后筛选下拉框选择如红框标注 + +![image-20230301104542343](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20230301104542343.png) + +起一个项目名称及选择项目位置,下一步 + +![image-20230301104644030](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20230301104644030.png) + +框架选择.Net 6.0(长期支持) + +选择启用Docker,为了之后可以部署到Docker容器 + +启用OpenAPI支持是为了可以输出Swagger接口文档,但如果使用Furion框架的话,需要勾掉 + +顶级语句是无需在Program.cs中显式包含Main方法,可以使用顶级语句功能最大程度地减少必须编写的代码 + +![image-20230301104855227](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20230301104855227.png) + +点击创建即可 + +![image-20230301105745182](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20230301105745182.png) + +# 集成Furion框架 + +在NuGet包管理器中搜索 `Furion` + +![image-20230301110105535](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20230301110105535.png) + +选择安装的项目,然后安装即可 + +`Program.cs`配置 + +```c# +var builder = WebApplication.CreateBuilder(args).Inject(); +builder.Services.AddControllers().AddInject(); +app.UseInject(); +``` + +# 可能遇到的问题 + +## 包降级 + +![image-20230301110232202](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20230301110232202.png) + +将提示的NuGet包升级到 前者的版本即可,比如图内的 Swashbuckle.AspNetCore 原有的版本是 6.2.3 那么升级到 6.5.0即可 + +# 部署到Docker + +## 安装.Net SDK 6.0环境 + +```shell +sudo rpm -Uvh https://packages.microsoft.com/config/centos/7/packages-microsoft-prod.rpm +sudo yum install dotnet-sdk-6.0 +dotnet --info +``` + +## Visual Studio添加Docker支持 + +![image-20221121144928205](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221121144928205.png) + +## Linux下构建Docker镜像 + +```shell +docker image build -f ./XiaodaERP/Dockerfile -t aspnetcore . +docker images +``` + +## 运行Docker镜像 + +```shell +docker run --name=aspnetcore -p 9001:80 -d aspnetcore +docker ps +``` + +```shell +cd /usr/local/jenkins_home/workspace/XiaodaERP_NetCore +echo $PWD +docker image build -f ./XiaodaERP/Dockerfile -t xiaodaerp/netcore . +docker images +docker run --name xiaodaerp/netcore -p 7274:80 -d xiaodaerp/netcore +``` + +# 部署到 IIS + +## IIS 介绍 + +Internet Information Services (IIS) 是一种灵活、安全且可管理的 Web 服务器,用于托管 Web 应用(包括 ASP.NET Core)。 + +## IIS 配置并安装 + +1. 打开控制面板 => 程序 => 启动或关闭Windows功能 + ![img](https://pic3.zhimg.com/80/v2-5be634b54f51cdee564f00e6dd4589d2_720w.webp)![img](https://pic1.zhimg.com/80/v2-78040e47692be8c8d36e94e059424c68_720w.webp) + +2. 选择Internet Information Services 安装 IIS + + > 注意:无需选中"设置"下的所有Internet Information Services,按照自己的需要选择安装即可 + + ![img](https://pic1.zhimg.com/80/v2-110ad0b9bc25c1b4614a81d5b69aba6c_720w.webp) + + 建议选择如下 + + ![image-20250402085450223](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20250402085450223.png) + + 第一次安装时间比较久,耐心等待一会。 + + ![img](https://pic3.zhimg.com/80/v2-e296862b914f6063b0fc7e7c169e992a_720w.webp) + + ![img](https://pic3.zhimg.com/80/v2-cdf21244638a844735d98ebfcc15fb26_720w.webp) + + 可能需要重启。 + +3. 验证 IIS 是否安装配置成功 + 找到Windows管理工具 => 打开IIS + ![img](https://pic1.zhimg.com/80/v2-c0c190c1555e95119854ab5f62db1564_720w.webp)运行 IIS 默认的 Default Web Site 查看是否正常 + + > **在C:\inetpub\wwwroot下IIS默认绑定了一个80端口的静态页面站点,我们将该站点浏览起来查看页面效果是否正常。** + > **站点地址:http://localhost:80** + + ![img](https://pic2.zhimg.com/80/v2-8a60e11f83611629c9840c97401de119_720w.webp) + + ![img](https://pic1.zhimg.com/80/v2-ac97c2f542c12f1acb69ade08badfd08_720w.webp) + + ![image-20230921092026202](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20230921092026202.png) + + 访问成功 + + + +## 安装 ASP.Net Core 模块/托管捆绑包 + +使用以下链接下载最新安装程序: + +[当前 .NET Core 托管捆绑包安装程序(直接下载)](https://dotnet.microsoft.com/permalink/dotnetcore-current-windows-runtime-bundle-installer) + +## 重新启动 IIS + +安装托管捆绑包后,可能需要手动重新启动 IIS。 例如,在运行 IIS 工作进程的路径上可能不存在 `dotnet` CLI 工具(命令)。 + +若要手动重启 IIS,请停止 Windows 进程激活服务 (WAS),然后重新启动 World Wide Web 发布服务 (W3SVC) 和所有相关服务。 在高级命令 shell 中执行以下命令: + +```powershell +net stop was /y +net start w3svc +``` + +安装完成后,打开如下页面,打开IIS管理器 + +选择左侧数的根目录后,在右侧选择模块,如下图所示。 + +![image-20230921092737808](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20230921092737808.png) + +安装完成后在模块中会显示`AspNetCoreModuleV2`模块 + +![image-20230921092956235](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20230921092956235.png) + +## 添加网站 + +网站=>右键添加网站 + +![image-20230921093121380](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20230921093121380.png) + +![image-20230921093331155](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20230921093331155.png) + +在上图中,我们创建了一个网站名为:**RDICoreWeb50**,并指定了到我们网站的发布路径。 + +这儿最重要的一步就是要设置网站的“应用程序池”。 + +在IIS左侧选择“应用程序池”,在右则可以看到我们网站使用的“**RDICoreWeb50**”,双击打开,并做如图配置: + +![image-20230921093515596](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20230921093515596.png) + +## 报错解决 + +**请求筛选模块被配置为拒绝包含的查询字符串过长的请求** + +ASP中配置行为 => 限制属性 => `最大请求实体主体限制` 1073741824 + +![image-20230921131734045](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20230921131734045.png) + +配置编辑器中选择节 => system.webServer/serverRuntime + +![image-20230921131850345](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20230921131850345.png) + +配置`maxRequestEntityAllowed`和`uploadReadAheadSize` 1073741824 + +![image-20230921131933569](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20230921131933569.png) + +项目目录`web.config`配置 + +```xml + + + + + + + + + + + + + + + + + + +``` + +## 远程发布到IIS + + + +# 托管到 Nginx + +## 配置 + +```conf + +#user nobody; +worker_processes 1; + +#error_log logs/error.log; +#error_log logs/error.log notice; +#error_log logs/error.log info; + +#pid logs/nginx.pid; + + +events { + worker_connections 1024; +} + + +http { + include mime.types; + 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 on; + #tcp_nopush on; + + #keepalive_timeout 0; + keepalive_timeout 65; + + #gzip on; + + # map $http_upgrade $connection_upgrade { + # default upgrade; + # '' close; + # } + + map $http_connection $connection_upgrade { + "~*Upgrade" $http_connection; + 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 { + 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:50; + 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:50/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; + } + + # 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; + #} + } + + + # 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; + # } + #} + +} + +``` + +其中有两个map块 + +1. 第一个`map`块: + ```conf + map $http_upgrade $connection_upgrade { + default upgrade; + '' close; + } + ``` + - 如果`$http_upgrade`的值与`default`不匹配(通常是指`$http_upgrade`未设置或未匹配任何其他条件),则将`$connection_upgrade`设置为`upgrade`。 + - 如果`$http_upgrade`的值为空字符串(''),则将`$connection_upgrade`设置为`close`。这意味着Nginx将关闭连接而不是升级。 + +2. 第二个`map`块(微软官方用法): + ```conf + map $http_connection $connection_upgrade { + "~*Upgrade" $http_connection; + default keep-alive; + } + ``` + + - 如果`$http_connection`的值匹配正则表达式`~*Upgrade`(不区分大小写地匹配包含"Upgrade"的值),则将`$connection_upgrade`设置为`$http_connection`的值,通常是`upgrade`。 + - 如果没有匹配的值,将`$connection_upgrade`设置为`keep-alive`。这意味着Nginx将保持HTTP连接保持活动状态以进行进一步的请求和响应。 + +总的来说,这两个`map`块都涉及控制HTTP升级的行为,但它们使用不同的条件来决定何时将`$connection_upgrade`设置为`upgrade`或`close`。第一个`map`块根据`$http_upgrade`的值设置,而第二个`map`块根据`$http_connection`的值设置,通过正则表达式检查是否包含"Upgrade"。 + +# 顶级语句配置 `Program.cs` + +## 取消默认JSON首字母小写命名 + +```c# +builder.Services.AddControllers().AddJsonOptions(options => { + options.JsonSerializerOptions.PropertyNamingPolicy = null; +}); +``` + +## Json序列化时忽略属性为null的值 + +```c# +builder.Services.AddControllers().AddJsonOptions(options => { + options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; +}); +``` + +## Json序列化时日期类型格式化输出 + +```c# +builder.Services.AddControllers().AddJsonOptions(options => +{ + options.JsonSerializerOptions.Converters.Add(new SystemTextJsonDateTimeJsonConverter("yyyy-MM-dd HH:mm:ss")); +}); +``` + +## 使用Autofac自动注入Service + +通过NuGet包管理器 安装NuGet包 + +![image-20221130161234399](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221130161234399.png) + +![image-20221130161319595](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221130161319595.png) + +Autofac + +Autofac.Extensions.DependencyInjection + +Autofac.Extras.DynamicProxy + +新建 `ServiceAutofac.cs`类 + +```c# +using System.Reflection; + +namespace XiaodaERP +{ + public class ServiceAutofac + { + /// + /// 获取程序集名称 + /// + /// + public static string GetAssemblyName() + { + return Assembly.GetExecutingAssembly().GetName().Name; + } + } +} +``` + +`Program.cs`配置 + +```c# +builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory()); +builder.Host.ConfigureContainer(builder => +{ + Assembly assembly = Assembly.Load(ServiceAutofac.GetAssemblyName());//注入Service程序集 可以是其他程序集 + builder.RegisterAssemblyTypes(assembly) + .AsImplementedInterfaces() + .InstancePerDependency(); +}); +``` + +## 注入Entity Framework Core 6 DbContext上下文 + +```c# +builder.Services.AddDbContext(options => +options.UseOracle(builder.Configuration.GetConnectionString("OracleDbContext"))); + +builder.Services.AddDbContext(options => options.UseSqlServer(builder.Configuration.GetConnectionString("SqlServerDbContext"))); +``` + +## 使用JWT进行授权与认证 + +安装NuGet包 + +`Microsoft.AspNetCore.Authentication.JwtBearer` + +![image-20221206130808039](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221206130808039.png) + +`appsettings.json`配置文件中配置 + +```json +"Authentication": { + "SecretKey": "nadjhfgkadshgoihfkajhkjdhsfaidkuahfhdksjaghidshyaukfhdjks", + "Issuer": "www.xiaoda", + "Audience": "www.xiaoda" + } +``` + +`Program.cs`顶级语句配置 + +```c# +// 使用Autofac自动注入Service +builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory()); +builder.Host.ConfigureContainer(builder => +{ + Assembly assembly = Assembly.Load(ServiceAutofac.GetAssemblyName());//注入Service程序集 可以是其他程序集 + builder.RegisterAssemblyTypes(assembly) + .AsImplementedInterfaces() + .InstancePerDependency(); + // 在IOC容器中注入 + // 用于Jwt的各种操作 + builder.RegisterType().InstancePerLifetimeScope(); + // 支持泛型存入Jwt + builder.RegisterType().InstancePerLifetimeScope(); +}); + +//JWT认证 +builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options => +{ + //取出私钥 + var secretByte = Encoding.UTF8.GetBytes(builder.Configuration["Authentication:SecretKey"]); + options.TokenValidationParameters = new TokenValidationParameters() + { + //验证发布者 + ValidateIssuer = true, + ValidIssuer = builder.Configuration["Authentication:Issuer"], + //验证接受者 + ValidateAudience = true, + ValidAudience = builder.Configuration["Authentication:Audience"], + //验证是否过期 + ValidateLifetime = true, + //验证私钥 + IssuerSigningKey = new SymmetricSecurityKey(secretByte) + }; + +}); +// 顺序不能颠倒 +// 你是谁 授权 +app.UseAuthentication(); +// 你可以干什么 验证 +app.UseAuthorization(); +``` + +新建 `TokenHelper.cs`工具类 + +```c# +using Microsoft.IdentityModel.Tokens; +using System.IdentityModel.Tokens.Jwt; +using System.Reflection; +using System.Security.Claims; +using System.Text; +using XiaodaERP.Models; + +namespace XiaodaERP.Utils +{ + public class TokenHelper + { + private readonly IConfiguration _configuration; + private readonly JwtSecurityTokenHandler _jwtSecurityTokenHandler; + public TokenHelper(IConfiguration configuration, JwtSecurityTokenHandler jwtSecurityTokenHandler) + { + this._configuration = configuration; + this._jwtSecurityTokenHandler = jwtSecurityTokenHandler; + } + public static string? Token { get; set; } + // 生成Token + public string CreateJwtToken(T user) + { + // 生成JWT + // Header,选择签名算法 + var signingAlogorithm = SecurityAlgorithms.HmacSha256; + // Payload,存放用户信息,放用户ID,用户名 + var claimList = this.CreateClaimList(user); + //Signature + //取出私钥并以utf8编码字节输出 + var secretByte = Encoding.UTF8.GetBytes(_configuration["Authentication:SecretKey"]); + //使用非对称算法对私钥进行加密 + var signingKey = new SymmetricSecurityKey(secretByte); + //使用HmacSha256来验证加密后的私钥生成数字签名 + var signingCredentials = new SigningCredentials(signingKey, signingAlogorithm); + //生成Token + var Token = new JwtSecurityToken( + issuer: _configuration["Authentication:Issuer"], //发布者 + audience: _configuration["Authentication:Audience"], //接收者 + claims: claimList, //存放的用户信息 + notBefore: DateTime.UtcNow, //发布时间 + expires: DateTime.UtcNow.AddMinutes(30), //有效期设置为1天 + signingCredentials //数字签名 + ); + //生成字符串token + var TokenStr = new JwtSecurityTokenHandler().WriteToken(Token); + return TokenStr; + } + // 获取Token Payload信息 + public T GetToken(string token) + { + Type t = typeof(T); + object obj = Activator.CreateInstance(t); + var b = _jwtSecurityTokenHandler.ReadJwtToken(token); + foreach (var item in b.Claims) + { + PropertyInfo propertyInfo = t.GetProperty(item.Type); + if (propertyInfo != null && propertyInfo.CanRead) + { + propertyInfo.SetValue(obj, item.Value, null); + } + } + return (T)obj; + } + // 根据类生成Token 断言列表 + private List CreateClaimList(T authUser) + { + var Class = typeof(T); + List claimList = new(); + foreach (var item in Class.GetProperties()) + { + // 不将PassWord放入Token中 + if (item.Name == "PassWord") + { + continue; + } + // 将UserName属性名重命名为username存入Token中 + if (item.Name == "UserName") + { + claimList.Add(new Claim("username", Convert.ToString(item.GetValue(authUser)))); + continue; + } + claimList.Add(new Claim(item.Name, Convert.ToString(item.GetValue(authUser)))); + } + return claimList; + } + } +} + +``` + +在登录方法中加入 + +```c# +public ViewUser Login(string UserName, string PassWord) +{ + var res = _sqlServerDbContext.Users.Include(user => user.Role).FirstOrDefault(x => x.UserName == UserName); + if (res != null) + { + if (res.PassWord == Md5Encoding(PassWord)) + { + // 生成JWT + var TokenStr = _tokenHelper.CreateJwtToken(res); + var config = new MapperConfiguration(cfg => cfg.CreateMap() + .ForMember(d => d.username, opt => opt.MapFrom(src => src.UserName)) + .AfterMap((src, des) => des.Roles = new Role[1] { src.Role }) + .AfterMap((src, des) => des.Token = "bearer " + TokenStr) // 需要加上bearer + .AfterMap((src, des) => des.HomePath = "/dashboard/analysis") + .AfterMap((src, des) => des.password = null)); + var mapper = config.CreateMapper(); + return mapper.Map(res); + } + } + return null; +} +``` + +WebAPI 需要认证的加上 `[Authorize]`注解,注意登录不能加 + +```c# +[AuthFilter] +[HttpPost(Name = "login")] +public ResultUtil Login(ViewUser viewUser) => + ResultUtil.ok(_userService.Login(viewUser.username, viewUser.password)); +// 需要认证的API +[Authorize] +[AuthFilter] +[HttpGet(Name = "getUserInfo")] +public ResultUtil GetUserInfo() +{ + Token = HttpContext.Request.Headers["Authorization"]; + Token = Token.Split(" ")[1]; + TokenHelper.Token = Token; + ViewUser us = _tokenHelper.GetToken(Token); + return ResultUtil.ok(us); +} +``` + +访问登录接口 + +![image-20221206131847333](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221206131847333.png) + +访问需要认证的接口,需要把Token放在请求头中,如果不携带Token,访问则报401 + +![image-20221206132042533](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221206132042533.png) + +请求头Key 为 Authorization + +访问成功 + +![image-20221206132124913](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221206132124913.png) + +# 三大拦截器 + +认证拦截器 `AuthorizeAttribute` + +方法拦截器 `ActionFilterAttribute` + +```c# +using Microsoft.AspNetCore.Mvc.Controllers; +using Microsoft.AspNetCore.Mvc.Filters; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System.IdentityModel.Tokens.Jwt; +using Castle.Core.Internal; +using XiaodaERP.Models; +using XiaodaERP.Utils; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; + +namespace XiaodaERP.Attributes +{ + public class AuthFilter : ActionFilterAttribute + { + //private readonly TokenHelper _tokenHelper; + //public AuthFilter(TokenHelper tokenHelper) + //{ + // this._tokenHelper = tokenHelper; + //} + private readonly SqlServerDbContext _sqlServerDbContext; + public AuthFilter(SqlServerDbContext sqlServerDbContext) + { + this._sqlServerDbContext = sqlServerDbContext; + } + + private SysActionLog sysActionLog = new() + { + ActionId = Guid.NewGuid().ToString().Replace("-", "").ToUpper() + }; + public override void OnActionExecuting(ActionExecutingContext context) + { + var descriptor = context.ActionDescriptor as ControllerActionDescriptor; + string param = string.Empty; + string globalParam = string.Empty; + + var jsonSetting = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }; + foreach (var arg in context.ActionArguments) + { + string value = Newtonsoft.Json.JsonConvert.SerializeObject(arg.Value, Formatting.None, jsonSetting); + param += $"{arg.Key} : {value} \r\n"; + globalParam += value; + } + // 方法名 + Console.WriteLine(descriptor.ActionName); + // 参数值拼接 + Console.WriteLine(globalParam); + // 参数名 与 值 + Console.WriteLine(param); + sysActionLog.ActionName = descriptor.ActionName; + sysActionLog.RequestParams = param; + } + + public override void OnActionExecuted(ActionExecutedContext context) + { + // 获取请求Host + Console.WriteLine(context.HttpContext.Request.Host); + sysActionLog.RequestHost = context.HttpContext.Request.Host.ToString(); + // 获取请求方法 + Console.WriteLine(context.HttpContext.Request.Method); + sysActionLog.RequestMethod = context.HttpContext.Request.Method; + // 获取请求Url + Console.WriteLine(context.HttpContext.Request.Path); + sysActionLog.RequestPath = context.HttpContext.Request.Path.ToString(); + // 获取应答返回状态码 + Console.WriteLine(context.HttpContext.Response.StatusCode); + if (context.HttpContext.Request.Path.Equals("/api/User/login")) + { + sysActionLog.ActionTime = DateTime.Now; + } + else + { + string Token = context.HttpContext.Request.Headers["Authorization"]; + // Token失效 + if (Token.IsNullOrEmpty()) + { + + } + else + { + Token = Token.Split(" ")[1]; + TokenHelper.Token = Token; + ViewUser us = new TokenHelper(new JwtSecurityTokenHandler()).GetToken(Token); + Console.WriteLine(us.UserId); + sysActionLog.ActionUserId = us.UserId; + Console.WriteLine(us.username); + sysActionLog.ActionUserName = us.username; + } + sysActionLog.ActionTime = DateTime.Now; + } + _sqlServerDbContext.SysActionLogs.Add(sysActionLog); + _sqlServerDbContext.SaveChanges(); + } + } +} + +``` + +接口上使用 + +```c# +//[AuthFilter] +[TypeFilter(typeof(AuthFilter))] +[HttpPost(Name = "login")] +public ResultUtil Login(ViewUser viewUser) => + ResultUtil.ok(_userService.Login(viewUser.username, viewUser.password)); + +[Authorize] +//[AuthFilter] // 注解为拦截器类名 +[TypeFilter(typeof(AuthFilter))] // 因为主键中使用了构造器依赖注入,所以需要使用TypeFilter,并需要在顶级语句中注入 AuthFilter +[HttpGet(Name = "getUserInfo")] +public ResultUtil GetUserInfo() +{ + Token = HttpContext.Request.Headers["Authorization"]; + Token = Token.Split(" ")[1]; + TokenHelper.Token = Token; + ViewUser us = _tokenHelper.GetToken(Token); + return ResultUtil.ok(us); +} +``` + +顶级语句中注入 + +```c# +builder.Services.AddScoped(); +``` + +异常拦截器 `ExceptionFilterAttribute` + +# AspNetCoreRateLimit 速率限制 + +## 介绍 + +[**AspNetCoreRateLimit**](https://github.com/stefanprodan/AspNetCoreRateLimit/)是一个ASP.NET Core速率限制的解决方案,旨在控制客户端根据IP地址或客户端ID向Web API或MVC应用发出的请求的速率。AspNetCoreRateLimit包含一个**IpRateLimitMiddleware**和**ClientRateLimitMiddleware**,每个中间件可以根据不同的场景配置限制允许IP或客户端,自定义这些限制策略,也可以将限制策略应用在每个API URL或具体的HTTP Method上。 + +## 使用 + +由上面介绍可知AspNetCoreRateLimit支持了两种方式:基于**客户端IP(\**IpRateLimitMiddleware)\**和客户端ID(\**ClientRateLimitMiddleware\**)速率限制** 接下来就分别说明使用方式 + +添加Nuget包引用: + +```shell +Install-Package AspNetCoreRateLimit +``` + +### 基于客户端IP速率限制 + +新建 `IPRateExtension.cs` + +```c# +public static class IPRateExtension + { + public static void AddIPRate(this IServiceCollection services, IConfiguration configuration) + { + if (services == null) throw new ArgumentNullException(nameof(services)); + + //从appsettings.json中加载常规配置,IpRateLimiting与配置文件中节点对应 + services.Configure(configuration.GetSection("IpRateLimiting")); + + //从appsettings.json中加载Ip规则 + services.Configure(configuration.GetSection("IpRateLimitPolicies")); + //注入计数器和规则存储 + //分布式部署时,需要将速率限制计算器和ip规则存储到分布式缓存中如Redis + services.AddSingleton(); + services.AddSingleton(); + // services.AddSingleton(); + services.AddSingleton(); + // services.AddSingleton(); + //配置(解析器、计数器密钥生成器) + services.AddSingleton(); + services.AddSingleton(); + } + } +``` + +`Program.cs` + +```c# +builder.Services.AddSingleton(); +//初始化限流器 +builder.Services.AddIPRate(builder.Configuration); +//启用客户端IP限制速率 +app.UseIpRateLimiting(); +``` + +**在appsettings.json中添加通用配置项节点:** + +```json +"IpRateLimiting": { + //false,则全局将应用限制,并且仅应用具有作为端点的规则*。例如,如果您设置每秒5次调用的限制,则对任何端点的任何HTTP调用都将计入该限制 + //true, 则限制将应用于每个端点,如{HTTP_Verb}{PATH}。例如,如果您为*:/api/values客户端设置每秒5个呼叫的限制, + "EnableEndpointRateLimiting": false, + //false,拒绝的API调用不会添加到调用次数计数器上;如 客户端每秒发出3个请求并且您设置了每秒一个调用的限制,则每分钟或每天计数器等其他限制将仅记录第一个调用,即成功的API调用。如果您希望被拒绝的API调用计入其他时间的显示(分钟,小时等) //,则必须设置StackBlockedRequests为true。 + "StackBlockedRequests": false, + //Kestrel 服务器背后是一个反向代理,如果你的代理服务器使用不同的页眉然后提取客户端IP X-Real-IP使用此选项来设置 + "RealIpHeader": "X-Real-IP", + //取白名单的客户端ID。如果此标头中存在客户端ID并且与ClientWhitelist中指定的值匹配,则不应用速率限制。 + "ClientIdHeader": "X-ClientId", + //限制状态码 + "HttpStatusCode": 429, + ////IP白名单:支持Ip v4和v6 + //"IpWhitelist": [ "127.0.0.1", "::1/10", "192.168.0.0/24" ], + ////端点白名单 + //"EndpointWhitelist": [ "get:/api/license", "*:/api/status" ], + ////客户端白名单 + //"ClientWhitelist": [ "dev-id-1", "dev-id-2" ], + //通用规则 + "GeneralRules": [ + { + //端点路径 + "Endpoint": "*", + //时间段,格式:{数字}{单位};可使用单位:s, m, h, d + "Period": "1s", + //限制 + "Limit": 2 + },   //15分钟只能调用100次 + {"Endpoint": "*","Period": "15m","Limit": 100},   //12H只能调用1000 + {"Endpoint": "*","Period": "12h","Limit": 1000},   //7天只能调用10000次 + {"Endpoint": "*","Period": "7d","Limit": 10000} + ] +} +``` + +配置节点已添加相应注释信息。 + +规则设置格式:    + +**端点格式:**`{HTTP_Verb}:{PATH}`,您可以使用asterix符号来定位任何HTTP谓词。 + +**期间格式:**`{INT}{PERIOD_TYPE}`,您可以使用以下期间类型之一:`s, m, h, d`。 + +**限制格式:**`{LONG}` + +**特点Ip限制规则设置,在\**appsettings.json中添加 IP规则配置节点\**** + +```json +"IpRateLimitPolicies": { + //ip规则 + "IpRules": [ + { + //IP + "Ip": "84.247.85.224", + //规则内容 + "Rules": [ + //1s请求10次 + {"Endpoint": "*","Period": "1s","Limit": 10}, + //15分钟请求200次 + {"Endpoint": "*","Period": "15m","Limit": 200} + ] + }, + { + //ip支持设置多个 + "Ip": "192.168.3.22/25", + "Rules": [ + //1秒请求5次 + {"Endpoint": "*","Period": "1s","Limit": 5}, + //15分钟请求150次 + {"Endpoint": "*","Period": "15m","Limit": 150}, + //12小时请求500次 + {"Endpoint": "*","Period": "12h","Limit": 500} + ] + } + ] +} +``` + +为使特点Ip限制规则生效,需初始化 IP 限制策略 + +`Program.cs` + +```c# +using (var serviceScope = app.Services.CreateScope()) +{ + var services = serviceScope.ServiceProvider; + + // get the IpPolicyStore instance + var ipPolicyStore = services.GetRequiredService(); + + // seed IP data from appsettings + ipPolicyStore.SeedAsync().GetAwaiter().GetResult(); + + var clientPolicyStore = services.GetRequiredService(); + clientPolicyStore.SeedAsync().GetAwaiter().GetResult(); +} +``` + +### 运行时更新速率限制 + +添加 `IpRateLimitController`控制器 + +```c# +/// +/// IP限制控制器 +/// +[Route("api/[controller]")] +[ApiController] +public class IpRateLimitController : ControllerBase +{ + private readonly IpRateLimitOptions _options; + private readonly IIpPolicyStore _ipPolicyStore; + + public IpRateLimitController(IOptions optionsAccessor, IIpPolicyStore ipPolicyStore) + { + _options = optionsAccessor.Value; + _ipPolicyStore = ipPolicyStore; + } + + [HttpGet] + public IpRateLimitPolicies Get() + { + return _ipPolicyStore.Get(_options.IpPolicyPrefix); + } + + [HttpPost] + public void Post() + { + var pol = _ipPolicyStore.Get(_options.IpPolicyPrefix); + + pol.IpRules.Add(new IpRateLimitPolicy + { + Ip = "8.8.4.4", + Rules = new List(new RateLimitRule[] { + new RateLimitRule { + Endpoint = "*:/api/testupdate", + Limit = 100, + Period = "1d" } + }) + }); + + _ipPolicyStore.Set(_options.IpPolicyPrefix, pol); + } +} +``` + +### 自定义 `IpRateLimitMiddleware`中间件 + +新建 `CustomIpRateLimitMiddleware`类并继承 `IpRateLimitMiddleware` + +```c# +public class CustomIpRateLimitMiddleware : IpRateLimitMiddleware +{ + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + + private readonly IIpRateLimitLogService _ipRateLimitLogService; + + public CustomIpRateLimitMiddleware(RequestDelegate next, IProcessingStrategy processingStrategy, + IOptions options, IIpPolicyStore policyStore, IRateLimitConfiguration config, + ILogger logger, IIpRateLimitLogService ipRateLimitLogService) : base(next, processingStrategy, options, policyStore, config, logger) + { + _ipRateLimitLogService = ipRateLimitLogService; + } + + // 重写 用于记录被阻止的请求的日志 + protected override void LogBlockedRequest(HttpContext httpContext, ClientRequestIdentity identity, + RateLimitCounter counter, RateLimitRule rule) + { + // base.LogBlockedRequest(httpContext, identity, counter, rule); + var nowDate = DateTime.Now; + var ipRateLimitLog = new IpRateLimitLog + { + HttpVerb = identity.HttpVerb, + Path = identity.Path, + ClientIp = identity.ClientIp, + Limit = rule.Limit, + Period = rule.Period, + Exceeded = counter.Count - rule.Limit, + Endpoint = rule.Endpoint, + CreateTime = nowDate + }; + var logStr = $"请求 {ipRateLimitLog.HttpVerb}:{ipRateLimitLog.Path} 来自 IP {ipRateLimitLog.ClientIp} 已被阻止, " + + $"配额 {ipRateLimitLog.Limit}/{ipRateLimitLog.Period} 超出次数 {ipRateLimitLog.Exceeded}. " + + $"被规则 {ipRateLimitLog.Endpoint} 阻止. 时间: {ipRateLimitLog.CreateTime}"; + Logger.Info(logStr); + _ipRateLimitLogService.InsertIpRateLimitLogAsync(ipRateLimitLog); + } +} +``` + +# Quartz.Net 定时任务 + +## 介绍 + +在项目的开发过程中,难免会遇见后需要后台处理的任务,例如定时发送邮件通知、后台处理耗时的数据处理等,这个时候你就需要`Quartz.Net`了。 + +`Quartz.Net`是纯净的,它是一个.Net程序集,是非常流行的Java作业调度系统Quartz的C#实现。 +`Quartz.Net`一款功能齐全的任务调度系统,从小型应用到大型企业级系统都能适用。功能齐全体现在触发器的多样性上面,即支持简单的定时器,也支持Cron表达式;即能执行重复的作业任务,也支持指定例外的日历;任务也可以是多样性的,只要继承IJob接口即可。 + +对于小型应用,`Quartz.Net`可以集成到你的系统中,对于企业级系统,它提供了Routing支持,提供了Group来组织和管理任务 + +## 使用 + +### Hello Quartz.Net + +添加Quartz.Net的引用 + +```shell +Install-Package Quartz -Version 3.7.0 +``` + +添加引用以后,来创建一个Job类`HelloQuartzJob` + +```c# +public class HelloQuartzJob : IJob +{ + public Task Execute(IJobExecutionContext context) + { + return Task.Factory.StartNew(() => + { + Console.WriteLine("Hello Quartz.Net"); + }); + } +} +``` + +这是个非常简单的Job类,它在执行时输出文本`Hello Quartz.Net`。 + +接下来,我们在程序启动时创建调度器(Scheduler),并添加HelloQuartzJob的调度: + +```c# +var schedulerFactory = new StdSchedulerFactory(); +var scheduler = await schedulerFactory.GetScheduler(); +await scheduler.Start(); +Console.WriteLine($"任务调度器已启动"); + +//创建作业和触发器 +var jobDetail = JobBuilder.Create().Build(); +var trigger = TriggerBuilder.Create() + .WithSimpleSchedule(m => { + m.WithRepeatCount(3).WithIntervalInSeconds(1); + }) + .Build(); + +//添加调度 +await scheduler.ScheduleJob(jobDetail, trigger); +``` +然后运行程序 + +```shell +任务调度器已启动 +Hello Quartz.Net +Hello Quartz.Net +Hello Quartz.Net +Hello Quartz.Net +``` + +通过演示可以看出,要执行一个定时任务,一般需要四步: + +1. 创建任务调度器。调度器通常在应用程序启动时创建,一个应用程序实例通常只需要一个调度器即可。 +2. 创建Job和JobDetail。Job是作业的类型,描述了作业是如何执行的,这个类是由我们定义的;JobDetail是Quartz对作业的封装,它包含Job类型,以及Job在执行时用到的数据,还包括是否要持久化、是否覆盖已存在的作业等选项。 +3. 创建触发器。触发器描述了在何时执行作业。 +4. 添加调度。当完成以上三步以后,就可以对作业进行调度了。 + +### 作业:Job和JobDetail + +Job是作业的类型,描述了作业是如何执行的,这个类型是由我们定义的,例如上文的`HelloQuartzJob`。Job实现IJob接口,而IJob接口只有一个`Execute`方法,参数`context`中包含了与当前上下文中关联的Scheduler、JobDetail、Trigger等。 + +一个典型的Job定义如下: + +```c# +public class HelloQuartzJob : IJob +{ + public Task Execute(IJobExecutionContext context) + { + return Task.Factory.StartNew(() => + { + Console.WriteLine("Hello Quartz.Net"); + }) + } +} +``` + +#### JobData + +Job不是孤立存在的,它需要执行的参数,这些参数如何传递进来呢?我们来定义一个Job类进行演示。 + +```c# +public class SayHelloJob : IJob +{ + public string UserName { get; set; } + + public Task Execute(IJobExecutionContext context) + { + return Task.Factory.StartNew(() => + { + Console.WriteLine($"Hello {UserName}!"); + }) + } +} +``` + +`SayHelloJob`在执行时需要参数`UserName`,这个参数被称为JobData,`Quartz.Net`通过JobDataMap的方式传递参数。代码如下: + +```C# +// 创建作业 +var jobDetail = JobBuilder.Create() + .SetJobData(new JobDataMap() { + new KeyValuePair("UserName", "Tom") + }).Build(); +``` + +通过JobBuilder的SetJobData方法,传入JobDataMap对象,JobDataMap对象中可以包含多个参数,这些参数可以映射到Job类的属性上。我们完善代码运行示例,可以看到如下: + +```shell +任务调度器已启动 +Hello Tom! +Hello Tom! +Hello Tom! +Hello Tom! +``` + +#### JobDetail + +JobDetail是Quartz.Net对作业的封装,它包含Job类型,以及Job在执行时用到的数据,还包括是否孤立存储、请求恢复作业等选项。 + +JobDetail是通过JobBuilder进行创建的。例如: + +```c# +var jobDetail = JobBuilder.Create() + .SetJobData(new JobDataMap(){ + new KeyValuePair("UserName", "Tom") + }) + .StoreDurably(true) + .RequestRecovery(true) + .WithIdentity("SayHelloJob-Tom", "DemoGroup") + .WithDescription("Say hello to Tom job") + .Build(); +``` + +**参数说明:** + +- SetJobData: 设置JobData +- StoreDurably: 孤立存储,指即使该JobDetail没有关联的Trigger,也会进行存储 +- RequestRecovery: 请求恢复,指应用崩溃后再次启动,会重新执行该作业 +- WithIdentity: 作业的描述信息 + +除此之外,`Quartz.Net`还支持两个非常有用的特性: + +- DisallowConcurrentExecution: 禁止并行执行,该特性是针对JobDetail生效的 +- PersistJobDataAfterExecution: 在执行完成后持久化JobData,该特性是针对Job类型生效的,意味着所有使用该Job的JobDetail都会在执行完成后持久化JobData。 + +# NLog 日志记录 + +## 介绍 + +NLog 是一个跨平台的 .Net 日志组件。 + +## 安装 + +### Package Manager + +```shell +Install-Package NLog +``` + +### .Net CLI + +```shell +dotnet add package NLog +``` + +## 配置 + +### 配置方法 + +NLog 可以通过两种方式进行配置: + +- 配置文件 +- 程序代码 + +### 通过配置文件配置 + +#### 配置文件路径 + +NLog 启动时会在某些标准路径查找配置文件,并进行自动配置: + +- 对于 **ASP.NET** 应用程序,以下文件会被查询: + - 标准 web 应用程序文件 web.config + - web.config 所在目录下的 web.nlog 文件 + - 应用程序目录下的 NLog.config 文件 + - NLog.dll 所在目录下的 NLog.dll.nlog 文件(only if NLog isn't installed in the GAC) + +#### 配置文件格式 + +NLog 支持两种文件格式: + +1. 在标准的 *.exe.config 或 web.config 中嵌入配置 +2. 在单独的文件中进行配置 + +个人觉得单独的配置文件便于在项目中切换配置或日志库。 + +NLog 配置文件是一个以`nlog`为根节点的 XML 文件。`nlog`节点可以添加命名空间,以开启 Visual Studio 的 Intellisense 功能。 + +```xml + + +``` + +以下元素可以作为`nlog`节点的子节点,前两种元素在所有 NLog 配置文件中都必须存在,剩余元数是可选的。 + +- `targets` - 定义日志目标/输出 +- `rules` - 定义日志路由规则 +- `extensions` - 定义要加载的 NLog 扩展性 *.dll 文件 +- `includes` - 定义要包含的外部配置文件 +- `variables` - 设置配置变量的值 + +在`nlog`节点中设置属性`autoReload="true"`, Nlog会监视配置文件,并在配置文件发送更改时自动载入配置文件而不需要重启应用程序。该功能支持通过`include`包含的子配置文件。 + +示例如下 + +```xml + + ... + +``` + +##### targets + +`targets`节点中定义了一系列日志输出目标,每一个输出目标是一个`target`元素。对于每一个`target`元素,`name`属性和`type`属性是必须要指定的: + +- `name` - `target`的名字。路由规则根据该属性将日志信息路由到当前目标。 +- `type` - `target`的类型。当使用了 xsi 命名空间时,该属性被命名为 xsi:type。目前 NLog 支持的输出目标列表:Targets + +不同类型的`target`节点可以接受不同的属性。例如对于`File`目标,`fileName`参数指定了日志文件的文件名;对于`Console`目标,`error`参数指定日志信息是写到标准错误流还是标准输出流。NLog 内置了许多预定义类型,也可以自定义输出目标类型,详见如何自定义输出目标。 + +以下是 `targets`节点的例子: + +```xml + + + + + + +``` + +###### 参数 + +**存档选项** + +- **archiveFileName** - 要用于存档的文件的名称。布局 + 它可能包含一个特殊的占位符{#},该占位符将被一系列数字替换,具体取决于存档策略。使用的哈希字符的数量决定了用于文件编号的数字位数。 + + > 警告:在NLog 4.5之前,如果在未指定特殊占位符{#}的情况下配置了`maxArchiveFiles`,则会导致非常激进的清理。NLog 4.4将从存档文件夹中删除任何文件(不仅仅是日志文件),因此请确保指定一个隔离文件夹。在NLog 4.5(和更高版本)中,清除通配符不是那么危险,并且允许相同的文件夹存档,这一点已经得到修复。 + +- **archiveNumbering** - 文件存档的编号方式。另请参阅归档编号示例 + 可能的值: + + - Rolling - 滚动样式编号(最近的始终是#0,然后是#1、...、#N) + + - Sequence - 序列样式编号。最新的档案编号最高。 + + - Date - 日期样式编号。日期的格式根据`archiveDateFormat` + + > 警告:在NLog版本之前。4.5.7这样的话,这将不能与`aiveAboveSize`一起工作。较新版本将正确合并到存档中的现有文件中。 + + - DateAndSequence - 日期和顺序的组合。档案将标记上一时期(年、月、日)的日期时间。最新的档案编号最高(与日期相结合)。日期的格式是根据`archiveDateFormat`的值设置的。 + +- **archiveEvery** - 指示是否在每次经过指定时间时自动存档日志文件。 + 可能的值: + + - Day - 每天存档。 + +- **archiveAboveSize** - 日志文件将自动存档的大小(以字节为单位)。Long + + > 在NLog版本之前。4.5.7那么这将不能与`filveNumbering=Date`一起使用。较新版本将正确合并到存档中的现有文件中。 + +- **maxArchiveFiles** - 应保留的最大存档文件数。如果MaxArchiveFiles小于或等于0,则不会删除旧文件,Integer 默认为:0 + +**性能调整选项** + +- **keepFileOpen** - 指示是否在每个日志记录事件时保持打开日志文件,而不是打开和关闭它。当设置为`false`时,它将允许其他应用程序“接管”文件句柄,并且NLog将无法记录到文件。当设置为`false`时,它将对性能造成重大影响,因为打开/关闭文件句柄的成本很高。Boolean 默认值:True(在NLog 5.0之前,默认值为`false`) + +##### rules + +`rules`节点是日志路由规则的集合,由一个或多个`logger`元素组成。每个`logger`元素记录了logger的名字、目标输出以及要处理的日志等级。NLog 从路由规则表的第一个`logger`开始处理,如果当前`logger`有效,则日志信息将被输出到指定的`target`。如果某个`logger`被标记为`final`,那么其后的`logger`都会被忽略。 + +`logger`包含下列属性: + +- `name` - logger的名字(可以使用通配符*) +- `minLevel` - 最小日志等级 +- `maxLevel` - 最大日志等级 +- `level` - 单一的日志等级 +- `levels` - 以逗号分割的日志等级列表 +- `writeTo` - 以逗号分割的输出目标列表 +- `final` - 标记当前规则为最后一条规则 +- `enabled` - 使能当前规则 + +如果在一条规则中定义了多个日志等级相关的属性(`level`, `levels`, `minLevel`和`maxLevel`), + +按照优先级只生效当前优先级最高的属性。等级相关属性优先级如下 + +1. `level` +2. `levels` +3. `minLevel`和`maxLevel` +4. 没有设置(所有等级的日志都会被记录) + +以下是`rules`节点的例子: + +```xml + + + + + + +``` + +1. 命名空间 `Name.Space` 下类 `Class1` 中高于 `Debug` 级别的日志信息将被写入输出目标 `f1` +2. 命名空间`Name.Space` 下类 `Class1` 中级别为 `Debug` 和 `Error` 的日志信息将被写入 `target:f1` +3. 命名空间 `Name.Space` 下所有类中的日志信息将被写入 `target:f3, f4` +4. 命名空间 `Name.Space` 下所有类中级别在`Debug` 和 `Error` 之间 (`Debug`,`Info`,`Warn` 和 `Error`) 的日志信息不被记录(因为没有指定属性`writeTo`)。由于标记了属性 `final`,之后的 `logger` 都会被忽略。 + +##### extensions + +`extensions`节点可以添加额外的NLog元包或自定义功能。`assembly`属性指定的被包含程序集不带后缀`.dll`。示例如下 + +```xml + + + + + + + + + + + +``` + +NLog 4.0 之后,与`NLog.dll`同目录下名如`NLog*.dll`的程序集(如`NLog.CustomTarget.dll`)会被自动加载。 + +##### Includes + +`include`节点指定当前配置文件包含多个子配置文件。通过`${}`语法可以使用环境变量,下例展示包含一个名为当前机器名的配置文件。 + +```xml + + ... + + ... + +``` + +NLog 4.4.2 之后可以使用通配符`*`指定多个文件。例如,`` + +##### variables + +`variable`元素定义了配置文件中需要用到的变量,一般用来表示复杂或者重复的表达式(例如文件名)。变量需要先定义后使用,否则配置文件将初始化失败。定义变量的语法如下: + +```xml + +``` + +定义变量之后,可以通过`${var}`语法来使用: + +```xml + + + + + + + +``` + +## 记录日志 + +### 获取`NLog.Logger`实例 + +通过`NLog.LogManager.GetCurrentClassLogger`方法或`NLog.LogManager.GetLogger`方法可以获得`NLog.Logger`实例。 + +1. `NLog.LogManager.GetCurrentClassLogger`方法 + 通过`NLog.LogManager.GetCurrentClassLogger`方法可以创建一个与所在类同名(包括namespace)的`NLog.Logger`的实例。 + + ```c# + var logger = NLog.LogManager.GetCurrentClassLogger(); + ``` + + + +2. `NLog.LogManager.GetLogger`方法 + 通过`NLog.LogManager.GetLogger("MyLogger")`方法可以显示地指定`NLog.Logger`的名称为MyLogger。 + + ```c# + var logger = NLog.LogManager.GetLogger("MyLogger"); + ``` + +### NLog 支持的日志级别 + +- `Trace` - very detailed logs,包含大量的信息,例如 protocol payloads。该级别一般仅在开发环境中启用。 +- `Debug` - debugging information, 比 `Trance` 级别稍微粗略,一般在生产环境中不启用。 +- `Info` - information messages,一般在生产环境中启用。 +- `Warn` - warning messages,一般用于可恢复或临时性错误的非关键问题。 +- `Error` - error messages,一般是异常信息。 +- `Fatal` - 非常严重的错误! + +### NLog 日志记录函数方法 + +在代码中通过以下方法分别进行不同等级的日志记录, + +```csharp +NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); +logger.Trace("Sample trace message"); +logger.Debug("Sample debug message"); +logger.Info("Sample informational message"); +logger.Warn("Sample warning message"); +logger.Error("Sample error message"); +logger.Fatal("Sample fatal error message"); +``` + +除此之外,`NLog.Logger` 还可以通过调用 `Log` 方法记录日志, + +```csharp +NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); +// log level is passed as the parameter. +logger.Log(LogLevel.Trace, "Sample trace message"); +logger.Log(LogLevel.Debug, "Sample debug message"); +logger.Log(LogLevel.Info, "Sample informational message"); +logger.Log(LogLevel.Warn, "Sample warning message"); +logger.Log(LogLevel.Error, "Sample error message"); +logger.Log(LogLevel.Fatal, "Sample fatal message"); +``` + +### 预定义和模板宏 Layouts and Layout Renderers + +#### 模板宏 `Layout Renderers` + +Layout 使用模板宏 Layout Renders 自定义日志输出的内容和格式。例如,大多数`target`使用的默认`SimpleLayout`如下 + +```xml +${longdate}|${level:uppercase=true}|${logger}|${message} +``` + +通过 `${}` 语法可以使用预定义的模版宏 Layout Renders。 +Layout Renders 的列表:[Layout Renders](https://github.com/NLog/NLog/wiki/Layout-Renderers)。 + +#### **预定义 `Layout`** + +\- [CsvLayout](https://link.zhihu.com/?target=https%3A//github.com/NLog/NLog/wiki/CsvLayout) - A specialized layout that renders CSV-formatted events. +\- [JsonLayout](https://link.zhihu.com/?target=https%3A//github.com/NLog/NLog/wiki/JsonLayout) - A specialized layout that renders to JSON. +\- [LayoutWithHeaderAndFooter](https://link.zhihu.com/?target=https%3A//github.com/NLog/NLog/wiki/LayoutWithHeaderAndFooter) - A specialized layout that supports header and footer. +\- [Log4JXmlEventLayout](https://link.zhihu.com/?target=https%3A//github.com/NLog/NLog/wiki/Log4JXmlEventLayout) - A specialized layout that renders Log4j-compatible XML events. +\- [SimpleLayout](https://link.zhihu.com/?target=https%3A//github.com/NLog/NLog/wiki/SimpleLayout) - Represents a string with embedded placeholders that can render contextual information. +\- [CompoundLayout](https://link.zhihu.com/?target=https%3A//github.com/NLog/NLog/wiki/CompoundLayout) - A layout containing one or more nested layouts. + +## 最佳实践 + +#### **`NLog.Logger` 应为类的静态变量** + +新建 `NLog.Logger` 对象消耗一定的开销,例如需要获取锁或者分配对象。因此推荐以下方式创建`NLog.Logger` + +```csharp +namespace MyNamespace +{ + public class MyClass + { + private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); + } +} +``` + +#### **应由 `NLog.Logger` 格式化日志** + +尽量避免直接使用字符串进行格式化。例如, + +```csharp +var message = "Hello" + "Earch"; +logger.Info(message); +``` + +推荐使用 `NLog.Logger` 对象进行格式化。例如, + +```csharp +logger.Info("Hello {0}", "Earth"); +``` + +`NLog.Logger` 对象可以推迟执行格式化操作的时机,从而减少开销。 + +#### **应将异常对象传递给 `NLog.Logger`** + +避免将异常作为字符串格式化的参数,而是显示的将异常作为参数传递给函数。例如, + +```csharp +try +{ +} +catch (Exception ex) +{ + logger.Error(ex, "Something bad happened"); +} +``` + +#### **开启配置文件的有效性验证** + +默认情况下,NLog 屏蔽了自身的所有异常,因此 NLog 出错时不会使应用程序崩溃。对于大多数应用程序,建议在 `nlog` 元素增加 `throwConfigExceptions="true"` 属性开启初始化配置时的异常捕获功能,以验证配置文件的有效性。 + +```xml + + + +``` + +> ⚠️注意:还有一个名为 `throwExceptions` 的属性,不应在生产环境中使用。它是为了单元测试和本地调试设计的。 + +#### **别忘了 Flush 日志信息** + +默认情况下,NLog 在程序关闭时会自动 flush。 Windows 限定 .NET 应用程序在程序终止前一定时间内进行关闭操作(一般是 2 秒)。如果 NLog 的 `target` 依赖于网络传输(例如 Http, Mail, Tcp),那么建议手动执行 Flush/Shutdown 操作。 +Mono/Linux 上运行的 .NET 应用程序在关闭前需要停止 Thread/Timer。如果未能完成这些操作,则会引发未处理异常、段错误以及其他难以预料的行为,同样建议手动执行 Flush/Shutdown 操作。 + +```csharp +NLog.LogManager.Shutdown(); // Flush and close down internal threads and timers +``` + +# 实时应用 + +## 概述 + +### 什么是 SignalR? + +用于简化向应用添加实时 Web 功能。实时 Web 功能使服务器端代码能够将内容推送到客户端。 + +适合 SignalR 的候选项: + +- 需要从 服务器进行高频率更新的应用。示例包括游戏、社交网络、投票、拍卖、地图和GPS应用。 +- 仪表盘和监视应用。示例包括公司仪表板、即时销售更新或旅行警报。 +- 协作应用。协作应用的示例包括白板应用和团队会议软件。 +- 需要通知的应用。社交网络、电子邮件、聊天、游戏、旅行警报和很多其他应用都需要使用通知。 + +SignalR 提供用于创建服务器到客户端远程过程调用(RPC)的API。RPC 从服务器端 .NET Core 代码调用客户端上的函数。提供多个受支持的平台,其中每个平台都有各自的客户端SDK。因此,RPC 调用所调用的编程语言有所不同。 + +以下是 ASP.NET Core SignalR 的一些功能: + +- 自动处理连接管理 +- 同时向所有连接的客户端发送消息。例如聊天室。 +- 向特定客户端或客户端组发送消息。 +- 对其进行缩放,以处理不断增加的流量。 +- SignalR 中心协议 + +### 传输 + +SignalR 支持以下用于处理实时通信的技术(按正常回退的顺序): + +- WebSockets +- Server-Sent Events +- 长轮询 + +SignalR 自动选择服务器和客户端能力范围内的最佳传输方法。 + +### 中心 + +SignalR 使用中心在客户端和服务器之间进行通信。 + +Hub是一种高级管道,允许客户端盒服务器相互调用方法。SignalR 自动处理跨计算机边界的调度,并允许客户端调用服务器上的方法,反之亦然。可以将强类型参数传递给方法,从而支持模型绑定。SignalR 退供两种内置中心协议:基于 JSON 的文本协议和基于 MessagePack 的二进制协议。与 JSON 相比,MessagePack 通常会创建更小的消息。旧版浏览器必须支持 XHR 级别才能提供 MessagePack 协议支持。 + +中心通过发送包含客户端方法的名称和参数的消息来调用客户端代码。作为方法参数发送的对象使用配置的协议进行反序列化。客户端尝试将名称与客户端代码中的方法匹配。当客户端找到匹配项时,它会调用该方法并将反序列化的参数数据传递给它。 + +# 远程过程调用应用 + +## 使用 gRPC 服务 + +### proto 文件 + +gRPC 使用协定优先方法进行 API 开发。默认情况下,协议缓冲区 (protobuf) 用作接口定义语言 (IDL)。`.proto`文件包含: + +- gRPC 服务的定义。 +- 在客户端与服务器之间发送的消息。 + +创建 greet.proto 文件: + +- 定义`Greeter`服务。 +- `Greeter`服务定义`SayHello`调用。 +- `SayHello`发送`HelloRequest`消息并接受`HelloReply`消息: + +```protobuf +syntax = "proto3"; + +option csharp_namespace = "GrpcGreeter"; + +package greet; + +// greeting 服务定义。 +service Greeter { + // 发送一个 greeting + rpc SayHello (HelloRequest) returns (HelloReply); +} + +// 包含用户名的请求消息。 +message HelloRequest { + string name = 1; +} + +// 包含Greetings的响应消息。 +message HelloReply { + string message = 1; +} +``` + +### 将`.proto`文件添加到 C# 应用 + +通过将`.proto`文件添加到``项组中,可将该文件包含在项目中: + +```xml + + + +``` + +默认情况下,``引用将生成具体的客户端和服务基类。可使用引用元素的`GrpcServices`特性来限制 C# 资产生成。有效`GrpcServices`选项如下: + +- `Both`(如果不存在,则为默认值) +- `Server` +- `Client` +- `None` + +### 对`.proto`文件的 C# 工具支持 + +需要工具包`Grpc.Tools`才能从`.proto`文件生成 C# 资产。生成的资产(文件): + +- 在每次生成项目时按需生成。 +- 不会添加到项目中或是嵌入到源代码管理中。 +- 是包含在 obj 目录中的生成工件。 + +服务器和客户端项目都需要此包。`Grpc.AspNetCore`元包中包含对`Grpc.Tools`的引用。服务器项目可以使用 Visual Studio 中的包管理器或通过将``添加到项目文件来添加`Grpc.AspNetCore`: + +```xml + +``` + +客户端项目应直接引用`Grpc.Tools`以及使用 gRPC 客户端所需的其他包。运行时不需要工具包,因此依赖性标记为`PrivateAssets="All"`: + +```xml + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + +``` + +### 生成的 C# 资产 + +工具包会生成表示在所包含`.proto`文件中定义的消息的 C# 类型。 + +对于服务器端资产,会生成抽象服务基类型。基类型包含`.proto`文件中所含的所有 gRPC 调用的定义。创建一个派生自此基类型并为 gRPC 调用实现逻辑的具体服务实现。对于`greet.proto`(前面所述的示例),会生成一个包含虚拟`SayHello`方法的抽象`GreeterBase`类型。具体实现`GreeterService`会替代该方法,并实现处理 gRPC 调用的逻辑。 + +```c# +public class GreeterService : Greeter.GreeterBase +{ + private readonly ILogger _logger; + public GreeterService(ILogger logger) + { + _logger = logger; + } + + public override Task SayHello(HelloRequest request, ServerCallContext context) + { + return Task.FromResult(new HelloReply + { + Message = "Hello " + request.Name + }); + } +} +``` + +对于客户端资产,会生成一个具体客户端类型。`.proto`文件中的 gRPC 调用会转换为具体类型中的方法,可以进行调用。对于`greet.proto`(前面所述的示例) ,会生成一个`GreetClient`类型。调用`GreeterClient.SayHelloAsync`以发起对服务器的gRPC调用。 + +```c# +// The port number must match the port of the gRPC server. +using var channel = GrpcChannel.ForAddress("https://localhost:7042"); +var client = new Greeter.GreeterClient(channel); +var reply = await client.SayHelloAsync( + new HelloRequest { Name = "GreeterClient" }); +Console.WriteLine("Greeting: " + reply.Message); +Console.WriteLine("Press any key to exit..."); +Console.ReadKey(); +``` + +默认情况下,会为``项组中包含的每个`.proto`文件都生成服务器和客户端资产。若要确保服务器项目中仅生成服务器资产,请将`GrpcServices`属性设置为`Server`。 + +```xml + + + +``` + +同样,该属性在客户端项目中设置为`Client`。 + +## 调用 gRPC 服务 + +### 客户端工厂集成 + +gRPC 与`HttpClientFactory`的集成提供了一种创建 gRPC 客户端的集中方式。它可用作配置独立 gRPC 客户端实例的替代方法。 + +工厂具有以下优势: + +- 提供了用于配置逻辑 gRPC 客户端实例的中心位置。 +- 可管理基础`HttpClientMessageHandler`的生存期。 +- 在 ASP.NET Core gRPC 服务中自动传播截止时间和取消。 + +#### 注册 gRPC 客户端 + +若要注册 gRPC 客户端,可在`Program.cs`中的应用入口点处的WebApplicationBuilder的实例中使用通用的`AddGrpcClient`扩展方法,并指定 gRPC 类型化客户端类和服务地址: + +```c# +builder.Services.AddGrpcClient(o => +{ + o.Address = new Uri("https://localhost:5001"); +}) +``` + +gRPC 客户端类型通过依赖性注入(DI)注册为暂时性。现在可以在由 DI 创建的类型中直接注入和使用客户端。ASP.NET Core MVC 控制器、SignalR 中心和 gRPC 服务是可以自动注入 gRPC 客户端的位置: + +```c# +public class AggregatorService : Aggregator.AggregatorBase +{ + private readonly Greeter.GreeterClient _client; + + public AggregatorService(Greeter.GreeterClient client) + { + _client = client; + } + + public override async Task SayHellos(HelloRequest request, + IServerStreamWriter responseStream, ServerCallContext context) + { + using (var call = _client.SayHellos(request)) + { + await foreach (var response in call.ResponseStream.ReadAllAsync()) + { + await responseStream.WriteAsync(response); + } + } + } +} +``` + +# Swagger + +## CustomSchemaIds + +CustomSchemaIds方法用于自定义SchemaId,Swashbuckle中的每个Schema都有唯一的Id,框架会使用这个Id匹配引用类型,因此这个Id不能重复。 + +  默认情况下,这个Id是根据类名得到的(不包含命名空间),因此,当我们有两个相同名称的类时,Swashbuckle就会报错:   + +``` +System.InvalidOperationException: Can't use schemaId "$XXXXX" for type "$XXXX.XXXX". The same schemaId is already used for type "$XXXX.XXXX.XXXX" +``` + +  就是类似上面的异常,一般时候我们都得去改类名,有点不爽,这时就可以使用这个方法自己自定义实现SchemaId的获取,比如,我们自定义实现使用类名的全限定名(包含命名空间)来生成SchemaId,上面的异常就没有了:    + +```c# +options.CustomSchemaIds(CustomSchemaIdSelector); + +string CustomSchemaIdSelector(Type modelType) +{ + if (!modelType.IsConstructedGenericType) return modelType.FullName.Replace("[]", "Array"); + + var prefix = modelType.GetGenericArguments() + .Select(genericArg => CustomSchemaIdSelector(genericArg)) + .Aggregate((previous, current) => previous + current); + + return prefix + modelType.FullName.Split('`').First(); +} +``` + +# 最小 API + +## 概述 + diff --git a/source/_posts/ArchLinux.md b/source/_posts/ArchLinux.md new file mode 100644 index 0000000..cf58ee7 --- /dev/null +++ b/source/_posts/ArchLinux.md @@ -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 | crictl(k8s) | ctr(containerd) | nerdctl(containerd) | +| ------------------- | ----------------- | -------------------------------- | ---------------------------- | --------------------- | +| 查看运行的容器 | 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。 diff --git a/source/_posts/C#.md b/source/_posts/C#.md new file mode 100644 index 0000000..6b97d06 --- /dev/null +++ b/source/_posts/C#.md @@ -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 + +### diff --git a/source/_posts/CSS.md b/source/_posts/CSS.md new file mode 100644 index 0000000..76428ae --- /dev/null +++ b/source/_posts/CSS.md @@ -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 +选择器 { + 属性: 值; +} +``` + +## 元素选择器 + +举个例子,如果我们想把网页上所有的`

`标题变成红色: + +```css +h1 { + color: red; +} +``` + +- `h1` 是 **选择器**(选中所有 h1 标签)。 +- `color` 是 **属性**(我们要改颜色)。 +- `red` 是 **值**(改成红色)。 + +这叫 **元素选择器**,因为它选中了所有叫 `h1` 的元素。没问题,我们继续!🚀 + +为了确保你完全掌握了刚才的逻辑,我先揭晓一下刚才小练习的答案。要把 `

` 标签的文字变成蓝色,代码是这样的: + +CSS + +假设你的 HTML 里有这样一个段落标签: + +```html +

我是一个普通的段落。

+``` + +如果想把这个段落的文字颜色改成 **蓝色 (blue)** + +```css +p { + color: blue; +} +``` + +很简单对吧?这叫 **元素选择器**,因为它选中了所有叫 `p` 的元素。 + +------ + +🔥 进阶:如何只选中“某一个”? + +在实际做网页时,我们通常不希望**所有**的段落都长得一样。比如,你可能希望有一个段落是特别的“警告信息”。 + +这时候,我们需要给 HTML 标签起个“名字”或者贴个“标签”。 + +1. **类 (Class)**: + - **作用**:就像给一群人穿上同样的制服。多个元素可以共用同一个 class。 + - **CSS 写法**:用 `.` (小圆点) 开头。 +2. **ID**: + - **作用**:就像身份证号。整个页面里,一个 ID 只能给一个元素用,是唯一的。 + - **CSS 写法**:用 `#` (井号) 开头。 + +## 类选择器 + +假设我们的 HTML 代码是这样的: + +```html +

注意:这是一个警告!

+ +

我只是一个路人甲。

+``` + +如果我们只想选中那个带有 `alert` 类的段落,把它的颜色变成 **红色 (red)**,CSS 选择器应该怎么写? + +**提示**:记得 Class 选择器要用 `.` 开头哦。 + +```css +.alert { + color: red; +} +``` + +## ID 选择器 + +比 Class 更“独一无二”的:**ID**。 + +在 CSS 里,我们要用 **井号 (#)** 来代表 ID。 + +```html +

欢迎来到我的主页

+``` + +如果我们想把这个标题的 **字体大小 (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 + + +我是普通链接 +``` + +选中 **`.nav` 里面的 `a` 标签**,并把它的颜色改成 **橙色 (orange)**。 + +**提示**:记得利用空格来表示层级关系哦! + +```css +.nav a { + color: orange; +} +``` + +## 伪类 (Pseudo-classes) + +现在你已经能控制元素“静止”时的样子了,接下来我们要让网页变得更有**互动感**。我们要学习的是 **伪类 (Pseudo-classes)**。 + +最常用的一个伪类是 `:hover`。 + +- **作用**:它是指“当鼠标**悬停**在元素上时”的那个瞬间。 +- **语法**:在选择器后面紧跟着写 `:hover` (注意:冒号前面**不能**有空格)。 + +```css +/* 例子:所有按钮在鼠标放上去时,背景变黑 */ +button:hover { + background-color: black; +} +``` + +```html + + +我是普通链接 +``` + +要求: 当鼠标**悬停**在 `.nav` 里面的 `a` 标签上时,把它的**文字颜色**变成 **绿色 (green)**。 + +**提示**:你需要把刚才学的“后代选择器”和现在的“伪类”结合起来写哦! + +```css +.nav a:hover { + color: green; +} +``` + +# 盒模型 + +既然你已经学会了怎么给元素“上色”,接下来我们要学怎么控制它们的“身材”和“距离”。 + +在 CSS 的世界里,**所有的元素(无论是图片、段落还是标题)本质上都是一个矩形的盒子**。 + +我们要控制这个盒子,主要靠这四个属性(由内向外): + +1. **内容 (Content)**:盒子的核心,就是文字或图片本身。 +2. **内边距 (Padding)**:内容和边框之间的空间。就像快递盒子里的气泡膜,保护内容不紧贴着纸箱壁。 +3. **边框 (Border)**:盒子的边缘。 +4. **外边距 (Margin)**:盒子和其他盒子之间的距离。就像人与人之间的社交距离。 + +## CSS Text(文本) + diff --git a/source/_posts/Claude-Code.md b/source/_posts/Claude-Code.md new file mode 100644 index 0000000..31f3f5b --- /dev/null +++ b/source/_posts/Claude-Code.md @@ -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 设置为环境变量避免每次输入 +- 不同模型的价格和能力有所不同,根据需要选择 diff --git a/source/_posts/Docker-Compose.md b/source/_posts/Docker-Compose.md new file mode 100644 index 0000000..446491a --- /dev/null +++ b/source/_posts/Docker-Compose.md @@ -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=: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 +``` + + + diff --git a/source/_posts/Docker.md b/source/_posts/Docker.md new file mode 100644 index 0000000..e76fd8d --- /dev/null +++ b/source/_posts/Docker.md @@ -0,0 +1,1584 @@ +--- +title: Docker +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 +--- +# Docker 简介 + +--- + +Docker:海豚,身上背着一堆集装箱 + +场景: + +传统的服务器 Docker +1G左右 几十兆几百兆 +CentOS占CPU Docker CPU引擎占用低 +1-2分钟 几秒 +安装软件 安装方便 +部署应用 部署应用,挂载,数据卷 +多个应用放到一起 每个应用服务都是一个容器,相互隔离 +一个独立的操作系统 必须依赖于操作系统,推荐使用Linux + +# Docker 安装 + +## CentOS 7 + +```shell +curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun +# 另一种方式 +curl -sSL https://get.daocloud.io/docker | sh +# 如上述两种方式都不行 +yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo +# 查看所有可安装版本 +yum list docker-ce --showduplicates | sort -r +# 安装 +yum install docker-ce-18.03.1.ce +``` + +## AlmaLinux CentOS 8 + +```shell +dnf clean all +dnf update +# 添加必要的Docker存储库 +dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo +# 找到Docker CE的可安装版本 +dnf list docker-ce --showduplicates | sort -r +# 安装Docker CE +dnf install docker-ce-3:24.0.7-1.el9 -y +# 镜像源配置 +vim /etc/docker/daemon.json +``` + +## Ubuntu + +```shell +# 安装前先卸载操作系统默认安装的docker, +sudo apt-get remove docker docker-engine docker.io containerd runc + +# 安装必要支持 +sudo apt install apt-transport-https ca-certificates curl software-properties-common gnupg lsb-release + +# 阿里源(推荐使用阿里的gpg KEY) +curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg + +# 添加 apt 源: +# 阿里apt源 +echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null + +# 更新源 +sudo apt update +sudo apt-get update + +# 安装最新版本的Docker +sudo apt install docker-ce docker-ce-cli containerd.io + +# 等待安装完成 + +# 查看Docker版本 +sudo docker version + +# 查看Docker运行状态 +sudo systemctl status docker + +# 可选安装Docker 命令补全工具(bash shell) +sudo apt-get install bash-completion + +sudo curl -L https://raw.githubusercontent.com/docker/docker-ce/master/components/cli/contrib/completion/bash/docker -o /etc/bash_completion.d/docker.sh + +source /etc/bash_completion.d/docker.sh + +``` + +安装docker后,执行docker ps命令时提示 +permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get "http://%2Fvar%2Frun%2Fdocker.sock/v1.24/containers/json": dial unix /var/run/docker.sock: connect: permission denied + +首先查看当前存在的用户组中是否存在 `docker`用户组 + +```bash + cat /etc/group | grep docker + +docker:x:988: +``` + +若不存在,则需要使用以下命令添加`docker`用户组 + +```shell +sudo groupadd docker +``` + +然后执行以下命令将当前用户加入到`docker`用户组中 + +```shell +sudo gpasswd -a $USER docker +``` + +更新用户组 + +```shell +newgrp docker +``` + +也可以直接编辑当前Shell环境变量 + +```shell +vim ~/.zshrc + +# 末尾添加 groupadd -f docker +groupadd -f docker +``` + + + +1、镜像:image。一个镜像代表一个软件。如:redis镜像,mysql镜像,tomcat镜像。。 + 特点:只读 +2、容器:container。一个镜像只要一启动,称之为启动了一个容器。 +3、仓库:repository。存储docker中的镜像具体位置 + 远程仓库:在全球范围内有一个远程仓库 + 本地仓库:当前自己机器中下载的镜像存储位置 + +Docker配置阿里云镜像加速 +https://www.cnblogs.com/LUA123/p/11401962.html + +# Docker 使用 + +## 查看 Docker 信息 + +```shell +docker info +``` + +## 查看 Docker 镜像 image + +```shell +docker images +``` + +## 查找镜像和下载 + +### 远程查找镜像 + +```shell +docker search ubuntu +``` + +### 查找容器的版本信息 + +在找到所需要的容器镜像的名称之后,通常需要进一步在docker的镜像源中查找该镜像的版本列表。由于docker本身没有直接提供查看版本的功能,因此在这里我们为大家提供了一个可以查看镜像版本的简单脚本docker-tags。我们生成docker-tags脚本并加入以下内容 , + +```shell +vim docker-tags +# 添加以下内容 +curl -s -S "https://registry.hub.docker.com/v2/repositories/library/$1/tags/?page=$2" | +sed -e 's/,/\n/g' -e 's/\[/\\\[\n/g' | +grep -E '"name":|"count":' | +sed -e 's/"//g' -e "s/name:/$1:/g" -e "s/{count:/$1总版本数-/" +``` + +docker-tags脚本编辑好之后,需要通过chmod修改文件权限才可以执行。在权限修改完成之后,就可以使用docker-tags脚本来查询相关镜像的最近版本信息了。 + +```shell +chmod 777 docker-tags +./docker-tags ubuntu +``` + +## Docker 修改镜像源 + +```shell +# 第一步:新建或编辑daemon.json +vim /etc/docker/daemon.json + +# 第二步:daemon.json中编辑如下 +{ + "registry-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" + ] +} + +# 第三步:重启docker +systemctl restart docker.service + +# 第四步:执行docker info查看是否修改成功 +docker info + +# Registry 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/ + +``` + + + +## Docker 镜像操作 + +### 安装镜像 + +MySQL 5.7 + +```shell +docker pull mysql:5.7 +``` + +### 删除镜像 + +若已有镜像用于容器,请先将容器删除 + +```shell +docker rm -f +docker image rm +docker rmi +# 强制删除镜像(已经创建容器的镜像) +docker rmi --force +``` + +### 查看镜像 + +```shell +docker inspect +``` + +## Docker 容器操作 + +### 运行容器 + +```shell +# 第一个8080是外部海豚上的 第二个8080是内部集装箱上的 +docker run -p 8080:8080 +``` + +-a stdin: 指定标准输入输出内容类型,可选 STDIN/STDOUT/STDERR 三项; +-d: 后台运行容器,并返回容器 ID; +-i: 以交互模式运行容器,通常与 -t 同时使用; +-P: 随机端口映射,容器内部端口随机映射到主机的端口 +-p: 指定端口映射,格式为:主机 (宿主) 端口:容器端口 +-t: 为容器重新分配一个伪输入终端,通常与 -i 同时使用; +--name="nginx-lb": 为容器指定一个名称; +--dns 8.8.8.8: 指定容器使用的 DNS 服务器,默认和宿主一致; +--dns-search example.com: 指定容器 DNS 搜索域名,默认和宿主一致; +-h "mars": 指定容器的 hostname; +-e username="ritchie": 设置环境变量; +--env-file=[]: 从指定文件读入环境变量; +--cpuset="0-2" or --cpuset="0,1,2": 绑定容器到指定 CPU 运行; +-m : 设置容器使用内存最大值; +--net="bridge": 指定容器的网络连接类型,支持 bridge/host/none/container: 四种类型; +--link=[]: 添加链接到另一个容器; +--expose=[]: 开放一个端口或一组端口; +--volume , -v 绑定一个卷 +--restart:重启策略: + +- **`no`** – 此策略永远不会自动启动容器。这是使用 `docker run` 创建的所有容器的默认策略。 +- **`always`** – Docker 将确保容器始终运行。如果容器停止,它将立即重新启动。您仍然可以使用 `docker stop` 手动停止容器,但 Docker 会在下次守护进程重新启动时将其恢复。 +- **`on-failure`** – 如果容器因错误而停止,它将重新启动。守护进程重启后,Docker 不会启动容器。 + + `on-failure` 重启策略允许您指定应尝试重试的次数。如果连续多次启动失败,Docker 将放弃并让容器处于停止状态。 + + ```sql + docker run httpd:latest --restart on-failure:5 + ``` + + 在此示例中,Docker 将在失败(非零退出代码)后尝试重新启动容器五次。如果容器在第五次尝试时启动失败,将不再尝试重试。此选项对于在没有手动干预的情况下不太可能解决持续启动错误的容器很有用。 +- **`unless-stopped`** – 其功能类似于`always`。不同之处在于,如果容器已被手动停止,Docker 将永远不会重新启动容器。 + + + +### 启动容器 + +```shell +docker start +``` + +### 停止容器 + +```shell +docker stop +``` + +### 重启容器 + +```shell +docker restart +``` + +### 删除容器 + +```shell +docker rm +``` + +### 强制删除容器(正在运行的容器) + +```shell +docker rm --force +# 或者 +docker rm -f +``` + +### 查看容器 + +```shell +docker inspect +``` + + + +## Docker 镜像保存和加载 + +### 本地保存 + +首先可以通过`docker save`命令可以将docker内部的一个或多个镜像导出成文件。下面的命令中先下载nginx,hello-world两个镜像,然后再将镜像导出到images.tar文件中。`docker save的格式为:docker save -o [导出文件名] [需要导出的镜像列表]...` + +```shell +docker pull hello-world +docker pull nginx +docker save -o images.tar nginx hello-world +ll images.tar +``` + +### 从本地加载镜像文件 + +接下来通过`docker load`命令将`images.tar`中的两个镜像导入回docker内部。即可实现在没有网络访问的情况更新镜像。docker load的格式为:`docker load -i [导入文件名]`。要注意的是:如果docker内部已经存在了相关的镜像,文件中的镜像会被忽略。 + +在镜像导入完毕之后,可以通过`docker images`进行验证。 + +```shell +docker load -i images.tar +docker images +``` + +## Docker 容器快照的导出和导入 + +### 容器快照的导出 + +当容器文件修改之后,可以通过`docker export`命令将容器以快照的形式导出到文件。其命令的格式为`docker export 容器名 > 快照文件名`。**导出容器会丢失历史记录和元数据,类似与快照。** + +```shell +docker export python-1 > python-snapshot.tar +ll python-snapshot.tar +``` + +### 容器快照的导入 + +对于通过`docker export`导出的容器快照文件。可以通过`docker import`命令将其导入到docker中,在这里需要特别注意的是:`docker import是以镜像而不是容器的形式导入快照`。也就是说导入的快照并不能直接运行,而是需要根据此快照镜像再次创建容器才可以使用。`docker import`命令的格式为`docker import 快照文件 导入镜像名称:版本号` **启动export与import命令导出导入的镜像必须加/bin/bash或者其他/bin/sh,否则会报错。** + +```shell +docker import python-snapshot.tar python-snapshot:latest +``` + +## Docker 容器迁移 + +### 导出容器镜像 + +A 服务器上 + +```shell +docker commit container_name new_image_name +docker save new_image_name > image.tar +``` + +### 导入容器镜像 + +迁移到 B 服务器上cd + +```shell +docker load < image.tar +``` + +### **检查容器[数据卷](https://zhida.zhihu.com/search?content_id=249112174&content_type=Article&match_order=1&q=数据卷&zhida_source=entity)** + +重点查看容器是否有数据卷。使用 `docker inspect` 检查 `Mounts` 部分,确认数据卷绑定信息: + +```json +"Mounts": [ + { + "Type": "bind", + "Source": "/path/on/host", + "Destination": "/path/in/container", + "Mode": "rw", + "RW": true, + "Propagation": "rprivate" + } +] +``` + +将A服务器宿主机的目录备份到B服务器上 + +## Docker 网络 network + + + +## Docker 容器与宿主机时间不同步 + +对于已创建的容器: + +```shell +docker cp /usr/share/zoneinfo/Asia/Shanghai :/etc/localtime +``` + +创建容器前 + +```shell +docker run -d -p 8080:80 -v /etc/localtime:/etc/localtime -v /etc/timezone:/etc/timezone +``` + +## 修改运行中的 Docker 容器的端口映射和挂载目录 + +方法一:删除原有容器,重建新容器 + +参考以上移除容器,启动容器 + +方法二:修改配置文件,重启docker服务 + +容器配置文件路径: + +/var/lib/docker/containers/[[hash_of_the_container]/hostconfig.json] + +其中的hashofthecontainer是docker镜像的hash值,可以通过docker ps或者docker inspect containername查看。(CONTAINER ID就可以看出来) + +可以到/var/lib/docker/containers目录下,ls -l + +```json +{"Binds":["/usr/docker/redis/redis-slave2.conf:/usr/local/etc/redis/redis.conf"],"ContainerIDFile":"","LogConfig":{"Type":"json-file","Config":{}},"NetworkMode":"default","PortBindings":{"6379/tcp":[{"HostIp":"","HostPort":"6381"}]},"RestartPolicy":{"Name":"no","MaximumRetryCount":0},"AutoRemove":false,"VolumeDriver":"","VolumesFrom":null,"CapAdd":null,"CapDrop":null,"Dns":[],"DnsOptions":[],"DnsSearch":[],"ExtraHosts":null,"GroupAdd":null,"IpcMode":"shareable","Cgroup":"","Links":null,"OomScoreAdj":0,"PidMode":"","Privileged":false,"PublishAllPorts":false,"ReadonlyRootfs":false,"SecurityOpt":null,"UTSMode":"","UsernsMode":"","ShmSize":67108864,"Runtime":"runc","ConsoleSize":[0,0],"Isolation":"","CpuShares":0,"Memory":0,"NanoCpus":0,"CgroupParent":"","BlkioWeight":0,"BlkioWeightDevice":[],"BlkioDeviceReadBps":null,"BlkioDeviceWriteBps":null,"BlkioDeviceReadIOps":null,"BlkioDeviceWriteIOps":null,"CpuPeriod":0,"CpuQuota":0,"CpuRealtimePeriod":0,"CpuRealtimeRuntime":0,"CpusetCpus":"","CpusetMems":"","Devices":[],"DeviceCgroupRules":null,"DiskQuota":0,"KernelMemory":0,"MemoryReservation":0,"MemorySwap":0,"MemorySwappiness":null,"OomKillDisable":false,"PidsLimit":0,"Ulimits":null,"CpuCount":0,"CpuPercent":0,"IOMaximumIOps":0,"IOMaximumBandwidth":0} +``` + +重启docker服务,再启动容器服务就可以了。 + +```shell +systemctl restart docker +docker ps -a +docker start +``` + +## Docker 动态修改 --restart 参数 + +--restart参数 + +- no 不自动重启 +- on-failure:重启次数 指定自动重启的失败次数,到达失败次数后不再重启 +- always 自动重启 + +修改线上容器--restart参数值 + +```shell +docker update --restart=no [容器名] +docker update --restart=always [容器名] +docker update --restart=on-failure:3 [容器名] +``` + + + +## Docker 的镜像迁移到另一台服务器 + +```shell +# 找到想要移动的docker容器ID +dockerCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES +58ac9237b040 redis:4.0.11 "docker-entrypoint.s…" 5 hours ago Up 2 hours 0.0.0.0:6380->6379/tcp redis-slave1 +454bb484f90a redis:4.0.11 "docker-entrypoint.s…" 5 hours ago Up 2 hours 0.0.0.0:6379->6379/tcp redis-master +ps -a + +# 提交变更,并且把容器保存成镜像,命名为redis-master +docker commit 454bb484f90a redis-master +# 把redis-master镜像保存成tar文件 +docker save redis-master > /root/mysoftware/docker-file/redis/redis-master.tar +# 拷贝到新的机器,执行load命令 +docker load < ./redis-master.tar +``` + +## Docker中 ``镜像的来历及快速删除操作 + +```shell +docker rmi -f $(docker images | grep "none" | awk '{print $3}') +``` + +来历:在docker反复build一个Dockerfile,并且使用相同tag(或者不用tag)会存留很多none镜像。第一次build生成一个image id, 再次build后,会重新生成一个image id, 命名和上一个一样,所以上一个镜像的tag和名字就会自动变成 ``。 + +## 实时查看日志 + +```shell +docker logs -f +``` + +## 解决Linux 下普通用户执行docker命令权限问题 + +```shell +sudo groupadd docker #添加用户组 +sudo gpasswd -a username docker #将当前用户添加至用户组 +newgrp docker #更新用户组 +``` + +## Docker Dockerfile + +### 什么是 Dockerfile? + +Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。 + +### 使用 Dockerfile 定制镜像 + +1. 定制一个 nginx 镜像(构建好的镜像内会有一个 /usr/share/nginx/html/index.html 文件) + 在一个空目录下,新建一个名为 Dockerfile 文件,并在文件内添加以下内容: + + ```dockerfile + FROM nginx + RUN echo '这是一个本地构建的nginx镜像' > /usr/share/nginx/html/index.html + ``` + +2. FROM 和 RUN 指令的作用 + **FROM**: 定制的镜像都是基于 FROM 的镜像,这里的 nginx 就是定制需要的基础镜像。后续的操作都是基于 nginx。 + **RUN**: 用于执行后面跟着的命令行命令。有以下俩种格式: + shell 格式: + + ```dockerfile + RUN <命令行命令> + # <命令行命令> 等同于,在终端操作的 shell 命令。 + ``` + + exec 格式: + + ```dockerfile + RUN ["可执行文件", "参数1", "参数2"] + # 例如: + # RUN ["./test.php", "dev", "offline"] 等价于 RUN ./test.php dev offline + ``` + + **注意**: Dockerfile 的指令每执行一次都会在 docker 上新建一层。所以过多无意义的层,会造成镜像膨胀过大。例如: + + ```dockerfile + FROM centos + RUN yum -y install wget + RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" + RUN tar -xvf redis.tar.gz + ``` + + 以上执行会创建 3 层镜像。可简化为以下格式: + + ```dockerfile + FROM centos + RUN yum -y install wget \ +     && wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \ +     && tar -xvf redis.tar.gz + ``` + + 如上,以 **&&** 符号连接命令,这样执行后,只会创建 1 层镜像。 + +### 开始构建镜像 + +在 Dockerfile 文件的存放目录下,执行构建动作。 + +以下示例,通过目录下的 Dockerfile 构建一个 nginx:v3(镜像名称:镜像标签)。 + +**注**: 最后的`.`代表本次执行的上下文路径,下一节会介绍。 + +```shell +docker build -t nginx:v3 . +``` + +![img](Docker/dockerfile2.png) + +### 上下文路径 + +上一节中,有提到指令最后一个 **.** 是上下文路径,那么什么是上下文路径呢? + +```shell +docker build -t nginx:v3 . +``` + +上下文路径,是指 docker 在构建镜像,有时候想要使用到本机的文件(比如复制),docker build 命令得知这个路径后,会将路径下的所有内容打包。 + +**解析**:由于 docker 的运行模式是 C/S。我们本机是 C,docker 引擎是 S。实际的构建过程是在 docker 引擎下完成的,所以这个时候无法用到我们本机的文件。这就需要把我们本机的指定目录下的文件一起打包提供给 docker 引擎使用。 + +如果未说明最后一个参数,那么默认上下文路径就是 Dockerfile 所在的位置。 + +**注意**:上下文路径下不要放无用的文件,因为会一起打包发送给 docker 引擎,如果文件过多会造成过程缓慢。 + +### 指令详解 + +| Dockerfile 指令 | 说明 | +| --------------- | ------------------------------------------------------------------ | +| FROM | 指定基础镜像,用于后续的指令构建。 | +| MAINTAINER | 指定Dockerfile的作者/维护者。(已启用,推荐使用LABEL指令) | +| LABEL | 添加镜像的元数据,使用键值对的形式。 | +| RUN | 在构建过程中在镜像中执行命令。 | +| CMD | 指定容器创建时的默认命令。(可以被覆盖) | +| ENTRYPOINT | 设置容器创建时的主要命令。(不可被覆盖) | +| EXPOSE | 声明容器运行时监听的特定网络端口。 | +| ENV | 在容器内部设置环境变量。 | +| ADD | 将文件、目录或远程URL复制到镜像中。 | +| COPY | 将文件或目录复制到镜像中。 | +| VOLUME | 为容器创建挂载点或声明卷。 | +| WORKDIR | 设置后续指令的工作目录。 | +| USER | 指定后续指令的用户上下文。 | +| ARG | 定义在构建过程中传递给构建器的变量,可使用"docker build"命令设置。 | +| ONBUILD | 当该镜像被用作另一个构建过程的基础时,添加触发器。 | +| STOPSIGNAL | 设置发送给容器以退出的系统调用信号。 | +| HEALTHCHECK | 定义周期性检查容器健康状态的命令。 | +| SHELL | 覆盖Docker中默认的shell,用于RUN、CMD和ENTRYPOINT。 | + +#### COPY + +复制指令,从上下文目录中复制文件或者目录到容器里指定路径。 + +格式: + +```dockerfile +COPY [--chown=:] <源路径1>... <目标路径> +COPY [--chown=:] ["<源路径1>",... "<目标路径>"] +``` + +**[--chown=:]**:可选参数,用户改变复制到容器内文件的拥有者和属组。 + +**<源路径>**:源文件或者源目录,这里可以是通配符表达式,其通配符规则要满足 Go 的 filepath.Match 规则。例如: + +```dockerfile +COPY hom* /mydir/ +COPY hom?.txt /mydir/ +``` + +**<目标路径>**:容器内的指定路径,该路径不用事先建好,路径不存在的话,会自动创建。 + +#### ADD + +ADD 指令和 COPY 的使用格类似(同样需求下,官方推荐使用 COPY)。功能也类似,不同之处如下: + +- ADD 的优点 + + + + + +### networks + +#### 使用已经存在的网络 + +```shell +docker network create flink-network +``` + +```yaml +version: "2.2" +services: + jobmanager: + image: flink: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: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: 2 + parallelism.default: 2 + networks: + - flink-network +networks: + flink-network: + external: true +``` + +### 构建 Dockerfile 时打印RUN命令的输出 + +```shell + # 在docker build命令最后加上此命令 --no-cache 不缓存 + --progress=plain --no-cache +``` + +# Docker 磁盘占用清理 + +## docker system 命令 + +它可以用于管理磁盘空间 + +**docker system df** 命令,类似于 Linux 上的 **df**命令,用于查看 Docker 的磁盘使用情况: + +```shell +docker system df +TYPE TOTAL ACTIVE SIZE RECLAIMABLE +Images 22 5 14.54GB 10.6GB (72%) +Containers 6 3 2.14GB 696.8MB (32%) +Local Volumes 53 5 6.317GB 5.935GB (93%) +Build Cache 12 0 0B 0B + +``` + +可知,Docker 镜像占用了 14.5GB 磁盘,Docker 容器占用了 2.14GB 磁盘,Docker 数据卷占用了 6.317GB 磁盘。 + +**docker system prune** 命令可以用于清理磁盘,删除关闭的容器、无用的数据卷和网络,以及 dangling 镜像(即无 tag 的镜像)。docker system prune -a 命令清理得更加彻底,可以将没有容器使用 Docker 镜像都删掉。注意,这两个命令会把你暂时关闭的容器,以及暂时没有用到的 Docker镜像都删掉了 + +执行 **docker system prune -a** 命令之后,Docker 占用的磁盘空间减少了很多 + +```shell +du -sh /var/lib/docker/containers/* | sort -rh | head -5 +cd /var/lib/docker/containers/eb52fa7d62ce52ab7a6153636a0ec89c7c8dce6f1a94dc86b2fbf0702368e82 + +truncate -s 0 *-json.log +``` + + + +## 手动清理 Docker 镜像/容器/数据卷 + +对于旧版的 Docker(版本 1.13 之前),是没有 docker system 命令的,因此需要进行手动清理。这里给出几个常用的命令 + +**删除所有关闭的容器** + +```shell +docker ps -a | grep Exit | cut -d ' ' -f 1 | xargs docker rm +``` + +**删除所有 dangling 镜像(即无 tag 的镜像)** + +```shell +docker rmi $(docker images | grep "^" | awk "{print $3}") +``` + +**删除所有 dangling 数据卷(即无用的 volume)** + +```shell +docker volume rm $(docker volume ls -qf dangling=true) +``` + +## 限制容器的日志大小 + +防止它再次爆满 + +配置 Docker 全局日志轮转策略 + +**编辑/新建 Docker 配置文件**:`/etc/docker/daemon.json` + +**添加 log-opts 配置**: (限制每个容器日志最大 100MB,最多保留 3 个文件) + +```json +{ + "log-driver": "json-file", + "log-opts": { + "max-size": "100m", + "max-file": "3" + } +} +``` + +(注意:如果文件里已有其他配置,请确保 JSON 格式正确,不要漏掉逗号) + +**重启 Docker 服务**: + +```shell +systemctl daemon-reload +systemctl restart docker +``` + +注意:此配置只对**新建**的容器生效。对于现有的 `1a28bcc...` 容器,你需要将其删除并重新运行 (`docker rm` -> `docker run`) 才能应用新策略。 + +## 清理容器日志 + +Docker 的所有相关文件,包括镜像、容器等都保存在 **/var/lib/docker** 目录中: + +```shell +du -sh /var/lib/docker/ +29G /var/lib/docker/ +``` + +使用**du**命令继续查看,可以定位到真正占用这么多磁盘的目录 + +使用**truncate**命令,可以将 nginx 容器的日志文件“清零”: + +```shell +truncate -s 0 /var/lib/docker/containers/a376aa694b22ee497f6fc9f7d15d943de91c853284f8f105ff5ad6c7ddae7a53/*-json.log +``` + +也可以清空文件内容,需要root权限 + +```shell +# 方法 1:使用 echo 重定向(推荐) +sh -c "echo '' > /var/lib/docker/containers/1a28bcc2e01033a9d83df7ac183cb128cdd81e40852befbb218459d04a054189/1a28bcc2e01033a9d83df7ac183cb128cdd81e40852befbb218459d04a054189-json.log" + +# 或者 方法 2:使用 cat /dev/null +sh -c "cat /dev/null > /var/lib/docker/containers/1a28bcc2e01033a9d83df7ac183cb128cdd81e40852befbb218459d04a054189/1a28bcc2e01033a9d83df7ac183cb128cdd81e40852befbb218459d04a054189-json.log" +``` + +**原理:** 这会将文件的大小截断为 0,但保持文件的 inode(文件句柄)不变。Docker 进程可以继续向这个文件写入新日志,而你立即获得释放的空间。 + +## 重启 Docker + +当我清理了镜像、容器以及数据卷之后,发现磁盘空间并没有减少。根据[Docker disk usage](https://github.com/moby/moby/issues/12265)提到过的建议,我重启了 Docker,发现**磁盘使用率从 83%降到了 19%**。根据高手[指点](https://github.com/moby/moby/issues/12265#issuecomment-316303769),这应该是与内核 3.13 相关的 BUG,导致 Docker 无法清理一些无用目录: + +> it's quite likely that for some reason when those container shutdown, docker couldn't remove the directory because the shm device was busy. This tends to happen often on 3.13 kernel. You may want to update it to the 4.4 version supported on trusty 14.04.5 LTS. + +> The reason it disappeared after a restart, is that daemon probably tried and succeeded to clean up left over data from stopped containers. + +我查看了一下内核版本,发现真的是 3.13: + +```bash +uname -r +3.13.0-86-generic +``` + +如果你的内核版本也是 3.13,而且清理磁盘没能成功,不妨重启一下 Docker。当然,这个晚上操作比较靠谱。 + +# Docker 迁移 + +## 从Docker迁移到Podman + +```bash +# 迁移步骤概要 +# 1. 安装Podman +sudo apt-get install podman + +# 2. 迁移Docker配置 +podman system migrate + +# 3. 迁移镜像(如需要) +docker save myimage | podman load + +# 4. 创建别名(可选) +echo "alias docker=podman" >> ~/.zshrc +source ~/.zshrc + +# 5. 验证功能等效性 +podman run --rm hello-world +``` + +## 从Docker迁移到nerdctl + +```bash +# 迁移步骤概要 +# 1. 安装containerd和nerdctl +sudo apt-get install containerd.io +sudo nerdctl install + +# 2. 配置nerdctl +sudo cp /etc/docker/daemon.json /etc/nerdctl/nerdctl.toml + +# 3. 迁移镜像(如需要) +docker save myimage | sudo nerdctl load + +# 4. 更新脚本和CI/CD流程 +# 将所有'docker'命令替换为'nerdctl' + +# 5. 验证功能等效性 +nerdctl run --rm hello-world +``` + + + +# Docker 实例 + +## Docker 启动 MariaDB + +拉取mariadb镜像 + +```shell +docker pull mariadb:10.2 +docker images +docker run --name mariadb10.2 -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mariadb:10.2 +``` + +## Docker Redis 主从复制集群搭建 + +拉取redis4.0.11镜像 + +```shell +docker pull redis:4.0.11 +``` + +编辑redis.conf + +redis-master.conf + +```shell +vim /root/mysoftware/docker-file/redis/redis-master.conf +bind 0.0.0.0 #让redis可以被任意ip访问 +daemonize yes #让redis服务后台运行 +# 如需设定密码 +requirepass masterpassword # 设定密码 +``` + +redis-slave1.conf + +```shell +vim /root/mysoftware/docker-file/redis/redis-slave1 +bind 0.0.0.0 #让redis可以被任意ip访问 +daemonize yes #让redis服务后台运行 +# 如需设定密码 +requirepass masterpassword # 设定密码 +# 设置主机地址端口 redis5.0以上为 replicaof +slaveof +# 查看master ip +docker inspect +# 主库有密码必需要配置,代表主库的访问密码 +masterauth +``` + +运行redis容器 + +redis-master + +```shell +# 运行服务 +docker run -it --name redis-master -v /usr/docker/redis/redis-master.conf:/usr/etc/redis/redis.conf -v /usr/docker/redis/data/:/data -d -p 35379:6379 redis:4.0.11 /bin/bash +# 进入容器 +docker exec -it redis-master bash +# 加载配置 +redis-server /usr/local/etc/redis/redis.conf +# 如报错 +mkdir -p /usr/redis/bin +# 测试连接 +redis-cli -a +``` + +redis-slave1 + +```shell +# 运行服务 +docker run -it --name redis-slave1 -v /usr/docker/redis/redis-slave1.conf:/usr/etc/redis/redis.conf -v /usr/docker/redis/data/:/data -d -p 35380:6379 redis:4.0.11 /bin/bash +# 进入容器 +docker exec -it redis-slave1 bash +# 加载配置 +redis-server /usr/local/etc/redis/redis.conf +# 如报错 Can't chdir to '/usr/redis/bin': No such file or directory +mkdir -p /usr/redis/bin +# 测试连接 +redis-cli +# 查看主从信息 +info Replication +# 密码认证 +auth +``` + +## Docker Redis 哨兵高可用集群搭建 + +## Docker 安装 code-server + +```shell +# 拉取镜像 +docker pull codercom/code-server +# 创建宿主机目录 +mkdir /usr/docker/codeserver +# 创建配置文件 +vim /usr/docker/codeserver/config.yaml + +bind-addr: 127.0.0.1:8080 +auth: password +password: 123456 +cert: false + +# 创建并启动容器 # -u 表示以 root用户运行 +docker run -d -u root -p 8088:8080 --name code-server -v /usr/docker/codeserver/config.yaml:/root/.config/code-server/config.yaml -v /usr/docker/codeserver:/home/code codercom/code-server +``` + +## Docker 安装 Jenkins + +搜索Jenkins镜像 + +```shell +docker search jenkins +``` + +![image-20221121125608385](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221121125608385.png) + +拉取镜像 + +```shell +docker pull jenkins/jenkins +``` + +查看镜像 + +```shell +docker images +``` + +![image-20221121131216876](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221121131216876.png) + +启动镜像 + +```shell +cd /usr/local +mkdir jenkins_home +docker run -d -uroot -p 8889:8080 -p 50000:50000 --name jenkins -v /usr/bin/docker:/usr/bin/docker -v /var/run/docker.sock:/var/run/docker.sock -v /etc/sysconfig/docker:/etc/sysconfig/docker -v /usr/local/jenkins_home:/var/jenkins_home -v /etc/localtime:/etc/localtime +-u root jenkins/jenkins +``` + +启动后查看日志 + +```shell +docker logs jenkins +``` + +![image-20221121131635658](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221121131635658.png)可以找到初始密码 + +## Docker 安装 Nginx + +```shell +docker pull nginx + +docker run -d \ +-p 80:80 \ +-p 443:443 \ +--name nginx \ +--privileged=true \ +--restart=always \ +-v /usr/local/docker/nginx/html:/usr/share/nginx/html \ +-v /usr/local/docker/nginx/conf/nginx.conf:/etc/nginx/nginx.conf \ +-v /usr/local/docker/nginx/conf/conf.d:/etc/nginx/conf.d \ +-v /usr/local/docker/nginx/logs:/var/log/nginx nginx:1.25.2 + + +ps aux | grep "nginx: worker process" | awk '{print $1}' +``` + +## Docker 安装 SQL Server + +```shell +mkdir /etc/sqlserver_data +chmod -R 777 /etc/sqlserver_data +docker pull mcr.microsoft.com/mssql/server +docker run -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=Wyd210213" -p 1433:1433 --memory 2000M --name sqlserver2022 --restart=always -v /etc/sqlserver_data:/var/opt/mssql -d mcr.microsoft.com/mssql/server +``` + +### 安装破解内存限制 + +建立构建自定义镜像文件夹 + +```shell +mkdir crack-tiny-mssqlserver +cd crack-tiny-mssqlserver +``` + +新建python2脚本 + +```shell +vim crack.py +``` + +```python +oldfile = open("sqlservr.old", "rb").read() +newfile = oldfile.replace("\x00\x94\x35\x77", "\x00\x80\x84\x1e") +open("sqlservr", "wb").write(newfile) +exit() +``` + +新建Dockerfile文件 + +```shell +vim Dockerfile +``` + +```dockerfile +FROM mcr.microsoft.com/mssql/server:2022-latest +USER root +RUN cd /opt/mssql/bin/ && mv sqlservr sqlservr.old +COPY crack.py /opt/mssql/bin/ +WORKDIR /opt/mssql/bin/ +RUN apt-get update && apt-get install -y python2 +RUN ls && python2 /opt/mssql/bin/crack.py && chmod -R 777 /opt/mssql/bin/sqlservr +``` + +在 **crack-tiny-mssqlserver** 目录下执行构建镜像 + +```shell +docker build -t linux-sqlserver:2022 . +``` + + +```shell +mkdir /usr/local/docker/linux-sqlserver2022 +docker run -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=Wyd210213" -e "MSSQL_COLLATION=Chinese_PRC_CI_AS" -p 11433:1433 --name linux-sqlserver2022 --restart=always -v /usr/local/docker/linux-sqlserver2022:/var/opt/mssql -v /etc/localtime:/etc/localtime:ro -e TZ="Asia/Shanghai" -d linux-sqlserver:2022 +``` + +## Docker 安装 PostgreSQL + +```shell +docker pull postgres:latest +docker run -d --name=pgsql -p 5432:5432 -e POSTGRES_PASSWORD=Wyd210213 postgres:latest + +docker run -d -p 15432:5432 --name postgres16 --restart=always -v /usr/local/docker/postgres16/pgdata:/var/lib/postgresql/data -e POSTGRES_PASSWORD=Wyd210213 postgres:16.8 + +``` + +## Docker 安装 Timescaledb + +```shell +docker run -d -p 15433:5432 --name timescaledb-pg16 --restart=always -v /usr/local/docker/timescaledb-pg16/pgdata/data:/var/lib/postgresql/data -v /etc/localtime:/etc/localtime:ro -e POSTGRES_PASSWORD=Wyd210213 -e TZ=Asia/Shanghai timescale/timescaledb:latest-pg16 +``` + + + +## Docker 安装 MySQL 8 + +```shell +docker run \ +-p 3306:3306 \ +--name mysql \ +--restart=always \ +-v /usr/local/docker/mysql/mysql-files:/var/lib/mysql-files \ +-v /usr/local/docker/mysql/conf:/etc/mysql \ +-v /usr/local/docker/mysql/logs:/var/log/mysql \ +-v /usr/local/docker/mysql/data:/var/lib/mysql \ +-v /etc/localtime:/etc/localtime \ +-e MYSQL_ROOT_PASSWORD=Wyd210213 \ +-e TZ=Asia/Shanghai \ +-d mysql:8.0.21 \ +--lower_case_table_names=1 +``` + +## Docker 安装 MySQL 5.7 + +```shell +mkdir -p /usr/local/docker/mysql57 +# 宿主机创建配置文件目录映射到容器 +mkdir -p /usr/local/docker/mysql57/conf #(需要在此目录下创建"conf.d"、"mysql.conf.d"两个目录) +mkdir -p /usr/local/docker/mysql57/conf/conf.d # (建议在此目录创建my.cnf文件并进行相关MySQL配置) +mkdir -p /usr/local/docker/mysql57/conf/mysql.conf.d + +docker run \ +-p 3306:3306 \ +--name mysql57 \ +--restart=always \ +-v /usr/local/docker/mysql57/mysql-files:/var/lib/mysql-files \ +-v /usr/local/docker/mysql57/conf:/etc/mysql \ +-v /usr/local/docker/mysql57/logs:/var/log/mysql \ +-v /usr/local/docker/mysql57/data:/var/lib/mysql \ +-v /etc/localtime:/etc/localtime \ +-e MYSQL_ROOT_PASSWORD=Wyd210213 \ +-e TZ=Asia/Shanghai \ +-d mysql:5.7.43 \ +--lower_case_table_names=1 +``` + +进入容器内 登录mysql + +```shell +docker exec -it mysql57 /bin/bash +mysql -uroot -p +# 若出现 mysql: [Warning] World-writable config file '/etc/mysql/my.cnf' is ignored. +# 查看 /etc/mysql/my.cnf 文件权限 +ls -l /etc/mysql/my.cnf +# 如果看到以下输出 +# -rwxrwxrwx 1 root root 1234 Jan 1 00:00 /etc/mysql/my.cnf +# 那么说明该配置文件确实是对所有人开放了读、写和执行权利。 +# 接着我们需要修改这个文件的权限,使其只有root用户有读写权,其他用户只有读的权限: +chmod 644 /etc/mysql/my.cnf +# 再次使用 ls -l命令查看'/etc/mysql/my.cnf'的权限 +ls -l /etc/mysql/my.cnf +# 看到以下输出,则修改成功 +# -rw-r--r-- 1 root root 1234 Jan 1 00:00 /etc/mysql/my.cnf +``` + +## Docker 安装 Redis 7 + +```shell +# 首先得需要redis.conf,否则会映射成文件夹 +docker run --restart=always \ +-p 6379:6379 \ +--name redis \ +-v /usr/local/docker/redis/redis.conf:/etc/redis/redis.conf \ +-v /usr/local/docker/redis/data:/data \ +-v /etc/localtime:/etc/localtime \ +-d redis:7.0.13 redis-server /etc/redis/redis.conf +``` + +## Docker 安装 SeaweedFS + +```shell +docker run -itd -p 9333:9333 -p 19333:19333 -v /opt/seaweed/mdir:/data --name weed_master chrislusf/seaweedfs master -defaultReplication=001 + +docker run -itd -p 9334:8080 -v /opt/seaweed/vdir1:/data --name weed_volume1 --link weed_master chrislusf/seaweedfs volume -max=30 -mserver="weed_master:9333" -port=8080 -dataCenter=dc1 -rack=rack1 + +docker run -itd -p 9335:8080 -v /opt/seaweed/vdir2:/data --name weed_volume2 --link weed_master chrislusf/seaweedfs volume -max=30 -mserver="weed_master:9333" -port=8080 -dataCenter=dc1 -rack=rack1 + +curl -X PUT -F file=@/home/back.png http://volume1.tonisf.com:88/4,01eea6857d + +docker run -itd -p 18080:8080 -p 8333:8333 -p 18888:8888 -p 9333:9333 -p 19333:19333 -v /opt/seaweed/mdir:/data --name weed_server chrislusf/seaweedfs server filter -defaultReplication=001 -s3 + + +``` + +## Docker 安装 Subversion + +拉取镜像 + +```shell +docker pull elleflorio/svn-server + +mkdir /usr/local/docker/svn +cd /usr/local/docker/svn +``` + +创建脚本启动临时容器 + +```shell +vim start.sh + +docker stop svn-test +docker rm svn-test +docker run --restart always --name svn-test -d -p 3690:3690 -p 18080:80 \ + -v /usr/local/docker/svn:/tmp/svn elleflorio/svn-server + +chmod +x start.sh +./start.sh +``` + +创建脚本进入容器 + +```shell +vim enter.sh + +docker exec -it svn-test /bin/sh + +chmod +x enter.sh +./enter.sh +``` + +进入容器 + +```shell +# 查看仓库配置文件 +cat /etc/apache2/conf.d/dav_svn.conf +#LoadModule dav_svn_module /usr/lib/apache2/mod_dav_svn.so +#LoadModule authz_svn_module /usr/lib/apache2/mod_authz_svn.so +# +# DAV svn +# SVNParentPath /home/svn +# SVNListParentPath On +# AuthType Basic +# AuthName "Subversion Repository" +# AuthUserFile /etc/subversion/passwd +# AuthzSVNAccessFile /etc/subversion/subversion-access-control +# Require valid-user +# 拷贝相关文件到tmp,同步到挂载点 +mkdir /tmp/svn/config /tmp/svn/svnadmin_data +cp /etc/subversion/* /tmp/svn/config +cp /opt/svnadmin/data/* /tmp/svn/svnadmin_data +exit +``` + +退出容器,再次编辑启动容器脚本 + +```shell +vim start.sh + +# 3690是svn server的默认端口,80是apache的默认端口 +docker stop svn-test +docker rm svn-test +docker run --restart always --name svn-test -d -p 3690:3690 -p 18080:80 \ + -v /usr/local/docker/svn/repo:/home/svn \ + -v /usr/local/docker/svn/config:/etc/subversion \ + -v /usr/local/docker/svn/svnadmin_data:/opt/svnadmin/data elleflorio/svn-server +# 更改挂载点后,重启容器 +./start.sh +./enter.sh +# 创建一个示例项目 +mkdir -p /home/svn/myrep +ls /home/svn/ +# myrepo +# 添加用户访问权限 +vi /etc/subversion/subversion-access-control + +[groups] +[/] +* = r +# 添加admin的读写权限 +admin = rw + +# 添加用户账号 +htpasswd -b /etc/subversion/passwd admin admin123 +# Adding password for user admin +exit +# 授予权限 +sudo chmod -R a+w /usr/local/docker/svn/config/* +sudo chmod -R a+w /usr/local/docker/svn/repo/* +sudo chmod -R a+w /usr/local/docker/svn/svnadmin_data/* + +# 访问Apache HTTP Server +http://ip:18080/ +# 服务器开放18080端口 +# 在浏览器地址后面加上svn, 即http://ip:18080/svn,会弹出提示框输入用户名和密码,登录成功后可查看项目目录 + +``` + +配置svnadmin(一个php写的svn管理工具) + +访问 http://ip:18080/svnadmin/ + +``` +Error: Could not copy configuration file template. Require write permission (777) to "data" folder and all containing files. + +#0 /opt/svnadmin/index.php(20): include_once() +#1 {main} +``` + +配置文件权限 + +```shell +chmod 777 /usr/local/docker/svn/svnadmin_data/ +``` + +刷新以下 + +按照 上面步骤里提到的`/etc/apache2/conf.d/dav_svn.conf的内容去填写,并点击test按钮验证,如下图,然后点击Save Configuration` + +```shell +LoadModule dav_svn_module /usr/lib/apache2/mod_dav_svn.so +LoadModule authz_svn_module /usr/lib/apache2/mod_authz_svn.so + + + DAV svn + SVNParentPath /home/svn + SVNListParentPath On + AuthType Basic + AuthName "Subversion Repository" + AuthUserFile /etc/subversion/passwd + AuthzSVNAccessFile /etc/subversion/subversion-access-control + Require valid-user +``` + +![image-20231121123737636](Docker/image-20231121123737636.png) + +- Subversion 授权文件: /etc/subversion/subversion-access-control + +- 用户身份验证文件 (SVNUserFile): /etc/subversion/passwd + +- 代码仓库的父目录 (SVNParentPath): /home/svn + + > 需要授予权限 + > + > 进入容器内 + > + > 执行 + > + > ```chmodshell + > chmod -R a+w /home/svn/ + > ``` + +- 'svn.exe' 或 'svn'可执行文件:/usr/bin/svn + +- 'svnadmin.exe' 或 'svnadmin' 可执行文件:/usr/bin/svnadmin + +保存设置就可以登录了 + +### 可修改日志信息及作者 + +```shell +./enter.sh + +cd /home/svn/myrepo +cd hooks +mv pre-revprop-change.tmpl pre-revprop-change +vi pre-revprop-change + +REPOS="$1" +REV="$2" +USER="$3" +PROPNAME="$4" +ACTION="$5" + +# 日志信息 +if [ "$ACTION" = "M" -a "$PROPNAME" = "svn:log" ]; then exit 0; fi +# 作者 +if [ "$ACTION" = "M" -a "$PROPNAME" = "svn:author" ]; then exit 0; fi +echo "Changing revision properties %PROPNAME% is prohibited" >&2 +exit 1 + +``` + +## Docker 安装 Lucky + +首先创建挂载目录 + +```shell +mkdir /usr/local/docker/lucky +``` + +创建目录 lucky + +```shell +mkdir lucky +cd lucky +``` + +创建 docker-compose.yml 文件 + +```shell +touch docker-compose.yml +vim docker-compose.yml +``` + +编辑内容 + +```yaml +version: '3' +services: + lucky: + image: gdy666/lucky:latest + container_name: lucky + ports: + - "16601:16601" + volumes: + - /usr/local/docker/lucky:/goodluck + restart: always + network_mode: host + +``` + +保存并在当前目录下 运行 + +```shell +docker compose up -d +``` + +## Docker 安装 VaultWarden + +首先创建挂载目录 + +```shell +mkdir -p /usr/local/docker/vaultwarden/data +``` + +创建目录 bitwarden-docker + +```shell +mkdir bitwarden-docker +cd bitwarden-docker +``` + +创建 docker-compose.yml 文件 + +```shell +touch docker-compose.yml +vim docker-compose.yml +``` + +编辑内容 + +```yaml +version: '3' + +services: + # 服务名称 + bitwarden: + # 镜像名称 + image: bitwardenrs/server:latest + # 容器名称 + container_name: vaultwarden + # 开机自动启动 + restart: always + # 指定容器内的 /data 目录挂载到宿主机的当前目录下的 /usr/local/docker/bitwarden/data 目录,这样你可以在宿主机上执行数据库的备份操作 + volumes: + - /usr/local/docker/vaultwarden/data:/data + # bitwarden 配置 + environment: + # 开启网页访问 + WEB_VAULT_ENABLED: 'true' + # 开启新用户注册,我们注册后关闭即可 + SIGNUPS_ALLOWED: 'true' + # 开启/关闭长连接 + WEBSOCKET_ENABLED: 'true' + # 日志文件 + LOG_FILE: /data/vaultwarden.log + # 数据库 mysql://bitwarden:password@172.17.0.1:3306/bitwarden + # mysql://<数据库用户名>:<数据库用户密码>@<数据库公网地址>:<数据库端口>/<数据库名称> + DATABASE_URL: mysql://bitwarden:password@172.17.0.1:3306/bitwarden + #自行决定是否配置smtp邮件服务 + # SMTP_HOST: 'smtp.163.com' + # SMTP_FROM: 'test@163.com' + # SMTP_PORT: '465' + # SMTP_SSL: 'true' + # SMTP_USERNAME: 'test@163.com' + # SMTP_PASSWORD: 'password' + # SMTP_EXPLICIT_TLS: 'true' + ports: + - 7006:80 + - 7007:3012 + network_mode: "bridge" +# networks: +# bridge: +# external: true +# name: bridge + +``` + +保存并在当前目录下 运行 + +```shell +docker compose up -d +``` + +## Docker 安装 Minio + +```shell +docker run -p 9000:9000 -p 9001:9001 \ +-d --restart=always \ +--name minio \ +-e "MINIO_ACCESS_KEY=admin" \ +-e "MINIO_SECRET_KEY=Wyd210213" \ +-v /usr/local/docker/minio/data:/data \ +-v /usr/local/docker/minio/config:/root/.minio \ +minio/minio server \ +/data \ +--console-address ":9001" + +``` + +## Docker 安装 Tomcat 9 + +```shell +docker run -d -p 8080:8080 --name tomcat9 \ +-v /usr/local/docker/tomcat9/webapps:/usr/local/tomcat/webapps \ +-v /usr/local/docker/tomcat9/logs:/usr/local/tomcat/logs \ +tomcat:9.0.41-jdk8-corretto +``` + +### Tomcat默认文档查看 + +进入容器 + +```shell +docker exec -it tomcat9 /bin/bash + +cd /usr/local/tomcat/ +ls +cp -r ./webapps.dist/* ./webapps +exit +``` + diff --git a/source/_posts/Docker/dockerfile2.png b/source/_posts/Docker/dockerfile2.png new file mode 100644 index 0000000..87c1698 Binary files /dev/null and b/source/_posts/Docker/dockerfile2.png differ diff --git a/source/_posts/Docker/image-20231101093813381.png b/source/_posts/Docker/image-20231101093813381.png new file mode 100644 index 0000000..9172612 Binary files /dev/null and b/source/_posts/Docker/image-20231101093813381.png differ diff --git a/source/_posts/Docker/image-20231121123737636.png b/source/_posts/Docker/image-20231121123737636.png new file mode 100644 index 0000000..891a2da Binary files /dev/null and b/source/_posts/Docker/image-20231121123737636.png differ diff --git a/source/_posts/Flink.md b/source/_posts/Flink.md new file mode 100644 index 0000000..f0ae2ed --- /dev/null +++ b/source/_posts/Flink.md @@ -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。 diff --git a/source/_posts/Git-Cherry-Pick.md b/source/_posts/Git-Cherry-Pick.md new file mode 100644 index 0000000..012084a --- /dev/null +++ b/source/_posts/Git-Cherry-Pick.md @@ -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 +git cherry-pick ... +git cherry-pick ^.. # 摘取一个范围(含 start 和 end) +``` + +- ``:要摘取的提交的完整或简短哈希值 +- 可以一次指定多个提交,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 ` 标记冲突已解决 + +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` 是一个**精准、灵活但需谨慎使用**的工具,适用于: + +- 紧急修复移植 +- 选择性功能集成 +- 跨仓库补丁同步 + +> 📌 **原则:用完即走,不作为日常集成手段。** diff --git a/source/_posts/Git.md b/source/_posts/Git.md new file mode 100644 index 0000000..b4e8e3d --- /dev/null +++ b/source/_posts/Git.md @@ -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 +``` + +## 创建 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 -- +``` + +## 撤回(重置)本地仓库提交 + +若想把已经提交到本地仓库的提交再撤回到本地,通常有几种情况和对应的命令。最常用的是 `git reset`。 + +`git reset` 命令可以用来将当前分支的 `HEAD` 指针移动到指定的提交,并根据不同的模式来处理**暂存区**和**工作目录**。 + +- ### 1. `git reset --soft ` + + + + - **作用:** 将 `HEAD` 指针移动到指定的 ``,但**保留**工作目录和暂存区的内容。这意味着你的文件会回到该 `` 时的状态,但是你当前的修改和暂存的修改都会保留。 + + - **适用场景:** 你提交了一个 commit,但是发现提交信息写错了,或者少提交了一些文件。你可以用 `--soft` 回退到上一个 commit,然后修改文件,重新提交。 + + - **示例:** + + ```Bash + git reset --soft HEAD~1 + ``` + + 这会将 `HEAD` 回退到上一个提交(`HEAD~1` 表示上一个提交)。 + +- ### 2. `git reset --mixed ` (默认模式) + + + + - **作用:** 将 `HEAD` 指针移动到指定的 ``,并**清空**暂存区,但**保留**工作目录的内容。这意味着你的文件会回到该 `` 时的状态,你当前的修改会保留在工作目录中(未暂存)。 + + - **适用场景:** 你提交了一个 commit,但是发现这个 commit 的内容不对,想撤销提交,并重新暂存和提交。 + + - **示例:** + + ```Bash + git reset HEAD~1 + # 或者 git reset --mixed HEAD~1 + ``` + + 这会将 `HEAD` 回退到上一个提交,并将暂存区清空,但保留文件在工作目录。 + + + +- ### 3. `git reset --hard ` + + + + - **作用:** 将 `HEAD` 指针移动到指定的 ``,并**清空**暂存区和**丢弃**工作目录的所有修改。这意味着你的仓库会完全回到该 `` 时的状态,所有在该 `` 之后的修改都会丢失,**这是最危险的操作**。 + + - **适用场景:** 你想完全放弃当前分支上的所有最新修改和提交,回到之前的某个干净的状态。 + + - **示例:** + + ```Bash + git reset --hard HEAD~1 + ``` + + 这会将 `HEAD` 回退到上一个提交,并丢弃所有工作目录和暂存区的修改。 + + + +## 如何找到 `` 的 ID? + + + +你可以使用 `git log` 命令来查看提交历史,并找到你想要回退到的提交的哈希值(commit ID)。 + +Bash + +``` +git log +``` + +`git log` 会显示类似这样的信息: + +``` +commit abcdef1234567890abcdef1234567890abcdef (HEAD -> master) +Author: Your Name +Date: Fri Aug 1 13:20:00 2025 +0800 + + 最新的提交 + +commit fedcba0987654321fedcba0987654321fedcba (origin/master) +Author: Another Name +Date: Thu Jul 31 10:00:00 2025 +0800 + + 之前的提交 +``` + +你可以复制 `commit` 后面的哈希值(例如 `fedcba0987654321...`)来替换上面的 ``。 + +## 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://@github.com//.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 )。这会创建一个新的提交来撤销该提交的修改,并且不会改变历史,是更安全的选择。 diff --git a/source/_posts/Git/2ceb11682cc7230cf1220ecb78e548b5.png b/source/_posts/Git/2ceb11682cc7230cf1220ecb78e548b5.png new file mode 100644 index 0000000..a525b9c Binary files /dev/null and b/source/_posts/Git/2ceb11682cc7230cf1220ecb78e548b5.png differ diff --git a/source/_posts/Git/76ad4bb4370c7ae798f7b92c25859901.png b/source/_posts/Git/76ad4bb4370c7ae798f7b92c25859901.png new file mode 100644 index 0000000..5a1694e Binary files /dev/null and b/source/_posts/Git/76ad4bb4370c7ae798f7b92c25859901.png differ diff --git a/source/_posts/Git/image-20250416142637966.png b/source/_posts/Git/image-20250416142637966.png new file mode 100644 index 0000000..1174e3e Binary files /dev/null and b/source/_posts/Git/image-20250416142637966.png differ diff --git a/source/_posts/Git/image-20250416151051085.png b/source/_posts/Git/image-20250416151051085.png new file mode 100644 index 0000000..6c44139 Binary files /dev/null and b/source/_posts/Git/image-20250416151051085.png differ diff --git a/source/_posts/Git/image-20250416151128595.png b/source/_posts/Git/image-20250416151128595.png new file mode 100644 index 0000000..04549c8 Binary files /dev/null and b/source/_posts/Git/image-20250416151128595.png differ diff --git a/source/_posts/Git/image-20250416151307312.png b/source/_posts/Git/image-20250416151307312.png new file mode 100644 index 0000000..e803ab1 Binary files /dev/null and b/source/_posts/Git/image-20250416151307312.png differ diff --git a/source/_posts/Git/image-20250416151416275.png b/source/_posts/Git/image-20250416151416275.png new file mode 100644 index 0000000..4a9c682 Binary files /dev/null and b/source/_posts/Git/image-20250416151416275.png differ diff --git a/source/_posts/Git/image-20250416151503685.png b/source/_posts/Git/image-20250416151503685.png new file mode 100644 index 0000000..d24ac0d Binary files /dev/null and b/source/_posts/Git/image-20250416151503685.png differ diff --git a/source/_posts/Hexo.md b/source/_posts/Hexo.md new file mode 100644 index 0000000..d7657b6 --- /dev/null +++ b/source/_posts/Hexo.md @@ -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] +``` + +新建一篇文章。如果没有设置 `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!= `©${theme.footer.owner.since} - ${nowYear} By ${config.author}` + else + .copyright!= `©${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}` +``` + diff --git a/source/_posts/Hexo/image-20250519143309765.png b/source/_posts/Hexo/image-20250519143309765.png new file mode 100644 index 0000000..be52278 Binary files /dev/null and b/source/_posts/Hexo/image-20250519143309765.png differ diff --git a/source/_posts/Hexo/image-20250519143418309.png b/source/_posts/Hexo/image-20250519143418309.png new file mode 100644 index 0000000..4503b38 Binary files /dev/null and b/source/_posts/Hexo/image-20250519143418309.png differ diff --git a/source/_posts/Hexo/image-20250519143457653.png b/source/_posts/Hexo/image-20250519143457653.png new file mode 100644 index 0000000..b4dc6f8 Binary files /dev/null and b/source/_posts/Hexo/image-20250519143457653.png differ diff --git a/source/_posts/Intellij Platform.md b/source/_posts/Intellij Platform.md new file mode 100644 index 0000000..36d6469 --- /dev/null +++ b/source/_posts/Intellij Platform.md @@ -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` – 跳到行首。 +- `$` – 跳到行尾。 \ No newline at end of file diff --git a/source/_posts/Java.md b/source/_posts/Java.md new file mode 100644 index 0000000..b40d548 --- /dev/null +++ b/source/_posts/Java.md @@ -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(...) 方法,说明了它是根据运行时需求即时创建并使用的。 diff --git a/source/_posts/JavaScript.md b/source/_posts/JavaScript.md new file mode 100644 index 0000000..ee9597b --- /dev/null +++ b/source/_posts/JavaScript.md @@ -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 的合并和复制 \ No newline at end of file diff --git a/source/_posts/Jenkins.md b/source/_posts/Jenkins.md new file mode 100644 index 0000000..4503334 --- /dev/null +++ b/source/_posts/Jenkins.md @@ -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. 点击 "应用" -> "确定" diff --git a/source/_posts/Kendo-UI.md b/source/_posts/Kendo-UI.md new file mode 100644 index 0000000..f81d7a4 --- /dev/null +++ b/source/_posts/Kendo-UI.md @@ -0,0 +1,5 @@ +--- +title: Kendo UI +date: 2024-03-08 11:06:51 +tags: +--- diff --git a/source/_posts/Kubernetes.md b/source/_posts/Kubernetes.md new file mode 100644 index 0000000..e95e5ce --- /dev/null +++ b/source/_posts/Kubernetes.md @@ -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 + +--- \ No newline at end of file diff --git a/source/_posts/Linux.md b/source/_posts/Linux.md new file mode 100644 index 0000000..3925802 --- /dev/null +++ b/source/_posts/Linux.md @@ -0,0 +1,3534 @@ +--- +title: Linux +date: 2021-04-07 16:04:58 +author: 文永达 +top_img: https://gcore.jsdelivr.net/gh/volantis-x/cdn-wallpaper/abstract/00E0F0ED-9F1C-407A-9AA6-545649D919F4.jpeg + +--- +# Linux + +## 简介 + +> 在linux系统中,没有盘符的概念。 +> 一个盘。/根目录 +> 没有图形化界面 +> 通过指令操作 +> +> linux指令是可以传参数的。 +> +> 在Linux系统下,万事万物皆文件。 + +## Linux的文件结构(19个) + +- bin:存放的是二进制的可以执行文件。(重点) +- sbin:存放的是二进制的可以执行文件。super,只有root用户才能访问 +- etc:存放系统配置文件(重点) +- usr:用来存放共享的系统资源 +- home:除了root用户的家目录(重点) +- root:就是root用户的家目录 +- dev:存放设备文件 + +## Linux指令 + +- Ctrl+u 键: 删除命令行开始至光标处 +- Ctrl+k 键: 删除光标至命令行结尾 +- Ctrl+a 键: 光标移到最前 +- Ctrl+e 键: 光标移到最后 +- ip addr(ip a):查看主机的ip地址 +- clear:清屏 +- tab 键: 提示作用。自动补全。 + +### 跳转目录: + +```shell +# 跳转指定目录 root目录下的www +cd /root/www +# 返回跳转前的目录 +cd - +# 跳转上一级目录 +cd ../ +# 跳转根目录 +cd / +# 跳转root目录 +cd ~ +# 跳转至主目录(后面跟个空格就行) +cd +``` + +### 复制粘贴: + +```shell +# 把aa.txt复制到init目录下 +cp aa.txt init/ +# 把init文件夹以及所包含的文件复制到 spring文件夹下 +cp -r init spring/ +``` + +### 列出目录内容: + +```shell +# 列出当前目录下的所有文件及目录(不含隐藏的) +ls +# 给ls指令传了一个参数l。等同于ll。列出当前目录下的所有文件及目录的详情。 +ls -l +# 列出当前目录下的所有文件及目录(含隐藏的) +ls -a +# ls后可以接目录名(相对路径),要么接绝对路径,查看目录下的文件及目录 +ls <当前目录下的文件夹> + +ls -l # 以长格式显示当前目录中的文件和目录 +ls -a # 显示当前目录中的所有文件和目录,包括隐藏文件 +ls -lh # 以人类可读的方式显示当前目录中的文件和目录大小 +ls -t # 按照修改时间排序显示当前目录中的文件和目录 +ls -R # 递归显示当前目录中的所有文件和子目录 +ls -l /etc/passwd # 显示/etc/passwd文件的详细信息 +``` + + +pwd:查看当前所在的目录 +方向键↑和↓:浏览历史指令 + +mkdir:创建目录 +mkdir -p:创建多级目录 +cp -r init spring:复制粘贴。把init目录以及包含的文件复制到spring目录下 +mv aa.txt xiaoqiang.txt:重命名。把aa.txt重命名为xiaoqiang.txt。 +mv bb.txt spring:移动。把bb.txt移动到spring目录。 +mv -f spring aaaaa:在覆盖前不提示 +mv -r aaaaa bbbbb:强行覆盖。前提是被覆盖的目录和覆盖的目录要结构相同。(慎用) +rm xiaoqiang.txt:删除xiaoqiang.txt +rm -f spring.xml:强行删除spring.xml,没有确认提示 +rm -r init:递归删除init目录 +rm -rf bbbbb:递归删除bbbbb并且没有确认提示(慎用) +rmdir aa:删除空目录aa(用的很少) +rm -rf *:删除所有的目录及文件(慎用) + +cat:显示文本文件的内容(一部分)。.java,.py,.c++,.xml,.html,.js,.css +more:分页显示文本文件的内容。只能向下查看,不能向上翻页。 +less:分页显示文本文件的内容。上下翻页。通过PgUp和PgDn进行上下翻页,↑和↓一行一行的查看。输入q退出查看。 +top -n 10 xxxx:查看文本文件的前10行 +tail -n 10 xxxx:查看文本文件的后10行 +tail -f xxxx:实时监控文本文件的变化 +Ctrl + c:几乎可以退出所有的操作 +echo:打印输出一句话。也可以用作向文本文件内写入信息。会自动追加并换行。 + +find: 查找文件夹或目录 find /usr -iname "\*docker\*" 查找/usr目录下 名称为docker 的文件或目录 模糊查询 并忽略大小写 + +### 压缩: + +1、打包,把多个文件打成一个包。 +2、压缩,把文件占用的大小进行压缩。 + +tar命令:用来进行压缩和解压缩的。 + -c 建立一个压缩文件(打包) + -x 解开一个压缩文件(解包) + -z 是否需要使用gzip压缩 + -v 压缩过程中是否显示文件日志 + -f 使用的文件名 +tar -cf:只打包,不压缩,不显示日志 +tar -xf:解压文件,不显示日志 +tar -cvf:只打包,不压缩,显示日志 +tar -xvf:解压文件,显示日志。 +tar -zcvf:打包压缩,显示日志 + +```shell +tar -zcvf wwwroot.tar.gz wwwroot/ +``` + +tar -zxvf:解压(最常用) + +```shell +tar -zxvf wwwroot.tar.gz +``` + +tar.gz 和 tgz 的区别 + +tar.gz 和 tgz 是两种常见的压缩文件格式,它们在本质上是相同的,只是文件扩展名不同。两者都是通过 tar 命令将多个文件打包成一个文件,然后再使用 gzip 压缩工具进行压缩。 + +### 系统服务: + +systemstl:操作系统服务。 + status:查看某个服务的状态 + stop:终止某个服务 + start:启动某个服务 + restart:重启某个服务 + +```shell +systemctl status mysqld +``` + +网络服务:network +防火墙服务:firewalld +Mysql:mysqld +systemctl status network + +### 网络状态: + +```shell +# -l或--listening 显示监控中的服务器的Socket +# -n或--numeric 直接使用IP地址,而不通过域名服务器。 +# -p或--programs 显示正在使用Socket的程序识别码和程序名称。 +netstat -lnp | grep 8080 +``` + +### 获取路径: + +```shell +readlink -f sample.txt /home/gliu/sample.txt +realpath -s sample.txt /home/gliu/sample.txt +find $(pwd) -name sample.txt /home/gliu/sample.txt +ls -l $PWD/sample.txt +``` + +### 文件目录: + +```shell +# 列出当前目录下所有文件的大小,以及所有文件大小的统计总和 +ls -lht +``` + + + +## 文件详情:(以home目录为例) + +d:说明当前文件是一个目录(- 代表的是文件) +rwx:r,可读;w,可写;x,可执行。(代表当前文件的创建者的权限) +r-x:代表的是和文件创建者的同组的用户的权限 +r-x:代表的是其他用户 +2:链接数 +root:创建者 +root:创建者所在的组 +6:文件占用空间的大小(字节) +Apr 11 2018:最后一次修改时间 +home:文件名或目录名 + +## 查找文件 + +### find 命令 + +基本格式:find path expression + +1. 按照文件名查找 + find / -name httpd.conf # 在根目录下 查找文件httpd.conf,表示在整个硬盘查找 + find /etc -name httpd.conf # 在 /etc 目录下查找文件 httpd.conf + find /etc -name '\*srm*' # 使用通配符\*(0或者任意多个)。表示在 /etc 目录下查找文件名中含有字符串'srm'的文件 +2. 按照文件特征查找 + find / -amin -10 # 查找在系统中最后10分钟访问的文件(access time) + find / atime -2 # 查找在系统中最后48小时访问的文件 + find / -empty # 查找在系统中为空的文件或者文件夹 + find / -group cat # 查找在系统中属于 group 为 cat 的文件 + find / -mmin -5 # 查找在系统中最后5分钟里修改过的文件(modify time) + find / -mtime -1 # 查找在系统中最后24小时里修改过的文件 + find / -user fred # 查找在系统中属于fred这个用户的文件 + find / -size +10000c # 查找出大鱼10000字节的文件(c:字节,w:双字, k:KB, M:MB, G:GB) + find / -size -1000k +3. 使用混合查找方式查找文件 + 参数有:!, -and(-a), -or(-0) + find /tmp -size +10000c -and -mtime +2 # 在/tmp目录下查找大于10000字节并在最后2分钟内修改的文件 + find / -user fred -or -user george # 在根目录下查找用户是fred或者george的文本文件 + find /tmp ! -user panda #在/tmp目录中查找所有不属于panda用户的文件 + +### du 命令 + +du 命令可以查看磁盘空间的使用情况,自然也可以用来查看磁盘上占用空间较多的文件和文件夹。 + +```shell +# 查找/root下5个最大的文件 +du -ah /root | sort -nr | head -n5 + +# 查找当前目录下最大的5个目录 +du -ah | sort -nr | head -n5 + +# 查找根目录下最大目录/文件(包括子文件夹) +du -Sh / | sort -rh | head -n10 + +# 只看大小在 GB 范围内的所有文件 +du -ah / | grep "[0-9]G\b" + +# 查看各目录占用(从根目录开始,排序显示) +du -sh /* 2>/dev/null | sort -rh | head -20 + +# 重点查看 /var 目录 +du -sh /var/* 2>/dev/null | sort -rh | head -20 +``` + +## 进程管理 + +ps 查看前台进程 +ps -aux 查看所有进程详细信息 UID 用户 PID 进程ID +ps -ef 查看所有进程详细信息 UID 用户 PID 进程ID PPID 父进程ID +父进程id为1 为系统进程 +top 动态显示进程 +ps -aux|grep network 查看所有进程详细信息,并搜索network进程 +kill 根据PID,终止进程 +kill -9 强制终止 + +## vi命令 编辑器 + +i 切换到编辑模式 +ESC 切换到命令模式 +:wq 保存并退出 +:q 不保存退出 +:w 保存 +:q! 强制不保存退出 +:wq! 强制保存退出 +a 在光标后插入 +A 在光标当前行的行尾插入 +i 在光标前插入 +I 在光标当前行的行头插入 +:set nu 显示行号 +:set nonu 取消显示行号 +gg 到文本的第一行 +G 到文本的最后一行 +u 后退一步 相当于 Ctrl + z +Ctrl + r 前进一步 +Shift + zz 保存退出 跟:wq一样 +起始行号,结束行号 del 删除对应范围内的行 + +安装vim编辑器 + +```shell +yum install -y vim +``` + +## 配置网络 + +```shell +cd /etc/sysconfig/network-scripts +vi ifcfg-ens33 + +TYPE=Ethernet +PROXY_METHOD=none +BROWSER_ONLY=no +BOOTPROTO=dhcp +DEFROUTE=yes +IPV4_FAILURE_FATAL=no +IPV6INIT=yes +IPV6_AUTOCONF=yes +IPV6_DEFROUTE=yes +IPV6_FAILURE_FATAL=no +IPV6_ADDR_GEN_MODE=stable-privacy +NAME=ens33 +UUID=08913b58-0bc2-42c5-8b59-6782e0029d7b +DEVICE=ens33 +ONBOOT=yes + +# 修改ONBOOT=yes +systemctl restart network + +ip addr +``` + +## 端口映射 + +```shell +# 将 80 端口 映射到 8080端口上 dport为目标端口 to-port为来源端口 +iptables -t nat -A PREROUTING -p tcp --dport 8080 -j REDIRECT --to-port 80 +# 查看iptables规则 +iptables -t nat -L -n -v + +## +Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes) + pkts bytes target prot opt in out source destination +12925 4377K DOCKER all -- * * 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL + 0 0 REDIRECT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:8080 redir ports 80 +``` + +## 软件安装 + +rpm 本地安装 +yum centos安装软件 需要源 + +安装screenFetch + +```shell +#使用wget 下载安装包 +wget https://github.com/KittyKatt/screenFetch/archive/master.zip +#使用unzip解压,unzip需要安装 +yum install unzip +unzip master.zip +#移动 +mv screenFetch-master/screenfetch-dev /usr/bin/screenfetch +``` + +## wget 下载工具 + +#### 格式 + +wget [参数] [URL地址] + +wget -O 图片名.png https://www.baidu.com/img/bd_logo1.png + +#### 记录和输入文件参数 + +| 短格式 | 长格式 | 说明 | +| ------ | ------------------- | ---------------------------------------------------- | +| -o | –output-file=FILE | 把记录写到FILE文件中 | +| -a | –append-output=FILE | 把记录追加到FILE文件中 | +| -d | –debug | 打印调试输出 | +| -q | –quiet | 安静模式(没有输出) | +| -v | –verbose | 冗长模式(这是缺省设置) | +| -nv | –non-verbose | 关掉冗长模式,但不是安静模式 | +| -i | –input-file=FILE | 下载在FILE文件中出现的URLs | +| -F | –force-html | 把输入文件当作HTML格式文件对待 | +| -B | –base=URL | 将URL作为在-F -i参数指定的文件中出现的相对链接的前缀 | +| | –sslcertfile=FILE | 可选客户端证书 | +| | –sslcertkey=KEYFILE | 可选客户端证书的KEYFILE | +| | –egd-file=FILE | 指定EGD socket的文件名 | + +#### 下载参数 + +| 短格式 | 长格式 | 说明 | +| ------ | --------------------- | -------------------------------------------------------- | +| | –bind-address=ADDRESS | 指定本地使用地址(主机名或IP,当本地有多个IP或名字时使用) | +| -t | –tries=NUMBER | 设定最大尝试链接次数(0 表示无限制). | +| -O | –output-document=FILE | 把文档写到FILE文件中 | +| -nc | –no-clobber | 不要覆盖存在的文件或使用.#前缀 | +| -c | –continue | 接着下载没下载完的文件 | +| | –progress=TYPE | 设定进程条标记 | +| -N | –timestamping | 不要重新下载文件除非比本地文件新 | +| -S | –server-response | 打印服务器的回应 | +| | –spider | 不下载任何东西 | +| -T | –timeout=SECONDS | 设定响应超时的秒数 | +| -w | –wait=SECONDS | 两次尝试之间间隔SECONDS秒 | +| | –waitretry=SECONDS | 在重新链接之间等待1…SECONDS秒 | +| | –random-wait | 在下载之间等待0…2*WAIT秒 | +| -Y | –proxy=on/off | 打开或关闭代理 | +| -Q | –quota=NUMBER | 设置下载的容量限制 | +| | –limit-rate=RATE | 限定下载速率 | + + + +## 用户 + +who am i 查看当前用户 +who --count 查看当前登录用户数量 +exit 退出登录 +groupadd 创建用户组 +groupdel 删除用户组 +useradd xiaoqiang -g user 创建一个用户xiaoqiang,并指定用户组user +passwd xiaoqiangf 给xiaoqiang用户指定密码 +su 切换用户 从root切换到其他用户不需要输入密码 如果从其他用户切换到root用户,需要输入密码 + +### 权限管理 + +Linux 下文件有三种权限 r 读 w写 x可执行 + +--- + +```shell +chmod 755 file +``` + +### 开启sudo权限 + +```shell +#添加sudo文件的写权限,命令是: +chmod u+w /etc/sudoers +#编辑sudoers文件 +vim /etc/sudoers +#找到这行 root ALL=(ALL) ALL,在他下面添加xxx ALL=(ALL) ALL (这里的xxx是你的用户名) +``` + +> ps:这里说下你可以sudoers添加下面四行中任意一条 +> +> ``` +> youuser ALL=(ALL) ALL +> %youuser ALL=(ALL) ALL +> youuser ALL=(ALL) NOPASSWD: ALL +> %youuser ALL=(ALL) NOPASSWD: ALL +> ``` +> 第一行:允许用户youuser执行sudo命令(需要输入密码). +> 第二行:允许用户组youuser里面的用户执行sudo命令(需要输入密码). +> 第三行:允许用户youuser执行sudo命令,并且在执行的时候不输入密码. +> 第四行:允许用户组youuser里面的用户执行sudo命令,并且在执行的时候不输入密码. + +```shell +#撤销sudoers文件写权限,命令: +chmod u-w /etc/sudoers +``` + +这样普通用户就可以使用sudo了. + +### 修改目录权限 + +**查看当前目录权限** + +```bash +sudo ls -ld /OLAP +``` + +输出实例: + +`drwxr-xr-x 5 root root 4096 Aug 5 08:27 /OLAP` + +**修改目录权限** + +使当前用户(假设为 `user`)能够对 `/OLAP`目录进行读写操作,可以将目录权限修改为`775`(即`rwxrwxr-x`): + +```bash +sudo chmod 775 /OLAP +``` + +这样,目录的所有者和所属组的用户都可以读写该目录,其他用户则由读取和执行权限。 + +**将当前用户加入目录所属组** + +如果目录所属组是`root`,可以将当前用户`user`加入`root`组(不推荐,因为`root`组权限过高): + +```bash +sudo usermod -aG root user +``` + +然后,重新登录或重启系统以使组变更生效。 + +### 更改目录的所有者 + +**查看当前目录的所有者** + +查看`/OLAP`目录的当前所有者: + +```bash +sudo ls -ld /OLAP +``` + +**更改目录的所有者** + +将`/OLAP`目录的所有者更改为当前用户(假设为`user`): + +```bash +sudo chown user:user /OLAP +``` + +这样,当前用户将拥有对该目录的完全控制权。 + +### 使用 ACL(访问控制列表) + +> ACL 提供了更细粒度的权限控制,允许为特定用户或组设置特定权限。 + +**安装 ACL 工具** + +在某些系统中,ACL 工具可能未默认安装,可以通过以下命令安装: + +```bash +sudo apt install acl -y +sudo dnf insyall acl -y +``` + +**设置 ACL 权限** + +为当前用户(假设为`user`)设置读写权限: + +```bash +sudo setfacl -m u:user:rwx /OLAP +``` + +这样,`user`用户将获得对`/OLAP`目录的读写权限,而不会影响其他用户的权限。 + +**验证 ACL 权限** + +查看当前目录的 ACL 权限: + +```bash +getfacl /OLAP +``` + +输出实例: +``` +getfacl: Removing leading '/' from absolute path names +# file: OLAP +# owner: user +# group: user +user::rwx +group::r-x +other::r-x +``` + +### 更改目录的默认权限(可选) + +如果要将新创建的文件和子目录自动继承特定权限,可以设置默认 ACL: + +```bash +sudo setfacl -dm u:user:rwx /OLAP +``` + +这样,新创建的文件和子目录将自动继承 `user` 用户的读写权限。 + +## Shell 脚本 + +### 为什么大多数 shell 脚本都包含 #! /bin/bash 在 shell 脚本的开头? + +“`#!/bin/bash`”这一行被称为`shebang` 行,在某些文献中,它被称为`hashbang` 行,这是因为它以两个字符`hash '#'` 和`bang '!' `开头。 + +```shell +#! /bin/bash + +echo 'Hello, World!' +``` + +当你在脚本的最顶部包含“`#!/bin/bash`”行时,系统知道你想使用 `bash` 作为脚本的解释器。因此,你现在可以直接运行 `hello.sh` 脚本,而无需在其前面加上 `bash`。 + +使用 `#!/bin/bash` 表示该脚本是 `bash shell` 脚本,无论系统上正在使用什么 `shell`,都应该使用 `bash` 作为解释器运行。如果你使用的是 `zsh` 特定的语法,你可以通过添加 `#! /bin/zsh` 作为脚本的第一行。 + +`#!` 和 `/bin/bash` 之间的空格无关紧要。你也可以使用 `#!/bin/bash`。 + +## yum + +### 改阿里源 + +```shell +yum install -y wget && mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup && wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo && yum clean all && yum makecache +``` + +## dnf + + + + + +## 安装MySQL + +有些Linux会自带MariaDB数据库,所以需要先卸载 + +列出安装的MariaDB的包 + +```shell +rpm -qa | grep mariadb +``` + +得到查看到的包名 + +卸载包 后面加上包名 + +```shell +rpm -e --nodeps mariadb-libs-5.5.68-1.el7.x86_64 +``` + +自此下载MariaDB已经完成了 + +切换到home目录下 + +```shell +cd /home/ +``` + +安装 wget 并下载yum库 + +```shell +yum install wget -y +wget https://repo.mysql.com//mysql80-community-release-el7-1.noarch.rpm +rpm -Uvh mysql80-community-release-el7-1.noarch.rpm +cd /etc/yum.repos.d/ +vim mysql-community.repo +``` + +选择要安装的MySQL版本 + +```shell +[mysql57-community] +name=MySQL 5.7 Community Server +baseurl=http://repo.mysql.com/yum/mysql-5.7-community/el/7/$basearch/ +enabled=0 // 将这里的0改为1 +gpgcheck=1 +gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql + +[mysql80-community] +name=MySQL 8.0 Community Server +baseurl=http://repo.mysql.com/yum/mysql-8.0-community/el/7/$basearch/ +enabled=1 //将这里的1改为0 +gpgcheck=1 +gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql +``` + +enabled=0为禁用对应版本的YUM库,enabled=1为启用,默认8.0的配置为enabled=1,安装5.7的话,我们就把8.0的enabled=1修改为enabled=0,然后把5.7的enabled=0修改为enabled=1 + +安装MySQL并启动 + +```shell +yum install mysql-community-server +service mysqld start +``` + +第一步安装成功之后,然后启动MySQL + +如果失败了,提示GPG + +RPM 维护一个单独的密钥环,因为它是一个系统范围的应用程序,而用户的 GPG 公钥环是一个用户特定的文件。要将 MySQL 公钥导入 RPM 密钥环,首先获取密钥,然后使用 rpm --import 导入密钥 + +```shell +# centos +rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2022 + +# Ubuntu: +wget -q -O - https://repo.mysql.com/RPM-GPG-KEY-mysql-2022 | apt-key add - + +yum install mysql-community-server +``` + +查看MySQL初始密码 + +```shell +sudo grep 'temporary password' /var/log/mysqld.log +``` + +登录MySQL + +```shell +mysql -u root -p +``` + +设置密码的验证强度等级 + +```shell +set global validate_password_policy=LOW; +``` + +设置密码长度为6位 + +```shell +set global validate_password_length=6; +``` + +修改MySQL初始密码 + +```shell +ALTER USER 'root'@'localhost' IDENTIFIED BY '123456'; +``` + +给root权限开启远程登录 + +```shell +use mysql; +select user,host from user; +update user set host = '%' where user = 'root'; +flush privileges; +``` + +--- + +### AlmaLinux 安装 (dnf包管理器方式) + +1. 首先确保系统是最新的。 + + ```shell + sudo dnf clean all + sudo dnf update + sudo dnf groupinstall "Development Tools" + ``` + +2. 安装 MySQL + 默认情况下,MySQL 在 AlmaLinux 9 基础存储库中可用。 只需使用以下命令安装 MySQL 服务器包 `dnf` 命令: + + ```shell + sudo dnf install mysql mysql-server + ``` + + 设置表名不区分大小写 + + ```shell + vim /etc/my.cnf.d/mysql-server.cnf + # 在 [mysqld] 中添加 + lower_case_table_names=1 + ``` + + 初始化后查询是否生效 + + ```mysql + show global variables like '%lower_case%'; + # lower_case_table_names 为 1 + ``` + + 启动 MySQL 服务并通过运行以下命令使其在启动时自动启动: + + ```shell + sudo systemctl status mysqld + sudo systemctl enable --now mysqld + ``` + + 确认安装并检查已安装的 MySQL 构建版本: + ```shell + mysql --version + ``` + +3. 在 AlmaLinux 9 上保护 MySQL。 + 默认情况下,MySQL 未加固。 您可以使用 `mysql_secure_installation` 脚本。 您应该仔细阅读以下每个步骤,这些步骤将设置 root 密码、删除匿名用户、禁止远程 root 登录、删除测试数据库和访问安全 MySQL: + + ```shell + mysql_secure_installation + ``` + + 对提示使用以下选项: + ```shell + Enter current password for root (enter for none): Just press the Enter + Set root password? [Y/n]: Y + New password: Enter your password + Re-enter new password: Repeat your password + Remove anonymous users? [Y/n]: Y + Disallow root login remotely? [Y/n]: Y + Remove test database and access to it? [Y/n]: Y + Reload privilege tables now? [Y/n]: Y + ``` + + 安全后,您可以使用以下命令登录 MySQL shell: + ```shell + sudo mysql -u root -p + ``` + + 要创建数据库、数据库用户并向数据库用户授予所有权限,请运行以下命令: + ```mysql + CREATE DATABASE test_db; + CREATE USER 'test_user'@'localhost' IDENTIFIED BY 'your-password'; + GRANT ALL ON tests_db.* TO 'test_user'@'localhost'; + FLUSH PRIVILEGES; + EXIT + ``` + + +### AlmaLinux 安装 (rpm手动方式) + +```shell +mkdir mysql_install +cd mysql_install +# 下载安装包 +wget https://cdn.mysql.com/archives/mysql-5.7/mysql-5.7.41-1.el7.x86_64.rpm-bundle.tar +tar -xvf mysql-5.7.41-1.el7.x86_64.rpm-bundle.tar + +rpm -ivh mysql-community-common-5.7.41-1.el7.x86_64.rpm + +rpm -ivh mysql-community-libs-5.7.41-1.el7.x86_64.rpm + +rpm -ivh mysql-community-libs-compat-5.7.41-1.el7.x86_64.rpm +# 如果出现以下错误 +error: Failed dependencies: + libcrypto.so.10()(64bit) is needed by mysql-community-libs-compat-5.7.41-1.el7.x86_64 + libcrypto.so.10(libcrypto.so.10)(64bit) is needed by mysql-community-libs-compat-5.7.41-1.el7.x86_64 + libssl.so.10()(64bit) is needed by mysql-community-libs-compat-5.7.41-1.el7.x86_64 + libssl.so.10(libssl.so.10)(64bit) is needed by mysql-community-libs-compat-5.7.41-1.el7.x86_64 +# 执行 +dnf install -y https://repo.almalinux.org/almalinux/8/AppStream/x86_64/os/Packages/compat-openssl10-1.0.2o-4.el8_6.x86_64.rpm + +rpm -ivh mysql-community-devel-5.7.41-1.el7.x86_64.rpm +# 如果出现以下错误 +error: Failed dependencies: + /usr/bin/pkg-config is needed by mysql-community-devel-5.7.41-1.el7.x86_64 +# 执行 +dnf install openssl-devel -y + +rpm -ivh mysql-community-client-5.7.41-1.el7.x86_64.rpm +# 如果出现以下错误 +错误:依赖检测失败: + libncurses.so.5()(64bit) 被 mysql-community-client-5.7.41-1.el7.x86_64 需要 + libtinfo.so.5()(64bit) 被 mysql-community-client-5.7.41-1.el7.x86_64 需要 +# 执行 +dnf install libncurses* -y +dnf install epel-release -y +dnf install ncurses-compat-libs -y + +rpm -ivh mysql-community-server-5.7.41-1.el7.x86_64.rpm +# 如果出现以下提示 +/usr/lib/tmpfiles.d/mysql.conf:23: Line references path below legacy directory /var/run/, updating /var/run/mysqld → /run/mysqld; please update the tmpfiles.d/ drop-in file accordingly. +# 执行 +vim /usr/lib/tmpfiles.d/mysql.conf +# 将/var/run/mysqld 改为 /run/mysqld + +# 如果出现以下提示 +error: Failed dependencies: + libcrypt.so.1()(64bit) is needed by mysql-community-server-5.7.41-1.el7.x86_64 + libcrypt.so.1(GLIBC_2.2.5)(64bit) is needed by mysql-community-server-5.7.41-1.el7.x86_64 +# 执行 +dnf install -y libxcrypt-compat + +# 如果出现以下提示 +error: Failed dependencies: + /usr/bin/perl is needed by mysql-community-server-5.7.41-1.el7.x86_64 + perl(Getopt::Long) is needed by mysql-community-server-5.7.41-1.el7.x86_64 + perl(strict) is needed by mysql-community-server-5.7.41-1.el7.x86_64 +dnf install -y perl.x86_64 +# 如果出现以下提示 +error: Failed dependencies: + net-tools is needed by mysql-community-server-5.7.41-1.el7.x86_64 +# 执行 +dnf install net-tools -y + +rpm -ivh mysql-community-embedded-compat-5.7.41-1.el7.x86_64.rpm + +``` + +**编辑配置文件** + +```shell +vim /etc/my.cnf + +# For advice on how to change settings please see +# http://dev.mysql.com/doc/refman/5.7/en/server-configuration-defaults.html +[client] +port = 3306 +user = mysql + +[mysqld] +# +# Remove leading # and set to the amount of RAM for the most important data +# cache in MySQL. Start at 70% of total RAM for dedicated server, else 10%. +# innodb_buffer_pool_size = 128M +# +# Remove leading # to turn on a very important data integrity option: logging +# changes to the binary log between backups. +# log_bin +# +# Remove leading # to set options mainly useful for reporting servers. +# The server defaults are faster for transactions and fast SELECTs. +# Adjust sizes as needed, experiment to find the optimal values. +# join_buffer_size = 128M +# sort_buffer_size = 2M +# read_rnd_buffer_size = 2M +basedir=/usr/local/mysql +datadir=/var/lib/mysql +socket=/var/lib/mysql/mysql.sock + +# Disabling symbolic-links is recommended to prevent assorted security risks +symbolic-links=0 + +log-error=/var/log/mysqld.log +pid-file=/var/run/mysqld/mysqld.pid + +max_connections = 400 +character-set-server = utf8mb4 +explicit_defaults_for_timestamp = true +lower_case_table_names = 1 +``` + +**初始化** + +```shell +mysqld --defaults-file=/etc/my.cnf --initialize-insecure --user=mysql +# 给mysql用户添加数据目录权限 +chown mysql:mysql /var/lib/mysql -R +systemctl start mysqld +systemctl enable mysqld + +# 查看root随机生成密码 +grep 'temporary password' /var/log/mysqld.log +# 若没有提示,则没有密码,可直接登录 +mysql -uroot +``` + +## 配置Java环境变量 + +将tar.gz格式的jdk解压后移动到/usr目录下 + +```shell +mv jdk1.8.0_301/ /usr/ +``` + +编辑/etc目录下profile文件 G到最后一行 + +```shell +vim /etc/profile + +export JAVA_HOME=/usr/jdk1.8.0_301 +export PATH=$PATH:$JAVA_HOME/bin + +``` + +:wq保存退出 + +重新加载配置文件 + +```shell +source /etc/profile +``` + +--- + +## CentOS7防火墙 + +放行特定端口 + +```shell +firewall-cmd --add-port=6379/tcp --permanent +``` + +移除放行端口 + +```shell +firewall-cmd --permanent --remove-port=8080/tcp +``` + +查询端口是否开放 + +```shell +firewall-cmd --query-port=8080/tcp +``` + +任何修改操作,配置完成后,需要重新加载firewall + +重新加载防火墙 + +```shell +firewall-cmd --reload +``` + +查看防火墙开放的端口 + +```shell +firewall-cmd --list-all +``` + +指定作用域开发防火墙端口 + +```shell +firewall-cmd --zone=public --add-port=3306/tcp --permanent +# -zone 作用域 +# -add-port 添加端口,格式为端口/协议 +# -permanent 永久生效,没有此参数重启后失效 + +``` + +## 安装与分区 + +Linux分区:顺序(主要是boot swap /) + +1,boot 分区(因为boot是引导启动的分区,所以分区的时候必须先分boot,通常设置大小为200M 空间足够300 - 500M) + +2,swap(缓存分区,通常设置大小为1G 通常是物理内存大小的2倍,比如你电脑是4G的物理内存,swap分区可以是8G) + +3,/ (根分区,通常把硬盘剩下所有的都分配给根/) + +4,/home分区 (可选) + +![image-20240227123457743](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20240227123457743.png) + +![image-20240227123607562](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20240227123607562.png) + +![image-20240227123637191](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20240227123637191.png) + +## 查看硬盘空间 + +```shell +# 统计磁盘整体情况,包括磁盘大小,已使用,可用 +df -lh +# 查看根目录下文件夹大小 +du -sh /* +``` + +## 快速查找大文件 + +```shell +# 查找当前用户目录下面大于100M的文件、需包含文件属性、以及从大到小排序: +find ~ -type f -size +100M | xargs ls -lhS +# 只显示文件大小和文件路径 +find ~ -type f -size +100M | xargs du -h | sort -hr +``` + +## 扩容 + +### LVM + +#### 旧有卷 + +将 `/home`部分空间合并至`/`下 + +```shell +df -h +``` + +![image-20240201162619794](Linux/image-20240201162619794.png) + +查看卷的文件系统类型 + +```shell +mount |grep home +``` + +![image-20240201162720183](Linux/image-20240201162720183.png) + +安装`xfsdump`备份工具 + +```shell +dnf install -y xfsdump +``` + +备份`/home` + +```shell +xfsdump -f ~/sdb_dump/ /home -M sdb_home -L sdb_home_1 +``` + +卸载`/home` + +```shell +umount /home/ +df -h +``` + +![image-20240201162906958](Linux/image-20240201162906958.png) + +移除 `/dev/mapper/almalinux-home`(删除前请确保重要文件已备份) + +```shell +lvremove /dev/mapper/almalinux-home +``` + +![image-20240201163026019](Linux/image-20240201163026019.png) + +```shell +lsblk +``` + +![image-20240201163052988](Linux/image-20240201163052988.png) + +扩展`/dev/mapper/almalinux-root`增加8.7G + +```shell +lvresize -L +8.7G /dev/mapper/almalinux-root +lsblk +``` + +![image-20240201163211759](Linux/image-20240201163211759.png) + +```shell +df -h +``` + +![image-20240201163229297](Linux/image-20240201163229297.png) + +扩展文件系统根目录 + +```shell +xfs_growfs / +``` + +![image-20240201163300368](Linux/image-20240201163300368.png) + +```shell +df -h +``` + +![image-20240201163342757](Linux/image-20240201163342757.png) + +重新创建`/dev/mapper/almalinux-home` + +```shell +lvcreate -L 9G -n home almalinux +lsblk +``` + +![image-20240201163506115](Linux/image-20240201163506115.png) + +将剩余空闲空间扩展给`/dev/mapper/almalinux-home` + +```shell +lvextend -l +100%FREE -n /dev/mapper/almalinux-home +lsblk +``` + +![image-20240201163544310](Linux/image-20240201163544310.png) + +格式化`/dev/mapper/almalinux-home` + +```shell +mkfs.xfs /dev/mapper/almalinux-home +``` + +![image-20240201163800217](Linux/image-20240201163800217.png) + +挂载 + +**因为lv名称和挂载点不变,因此无需修改/etc/fstab** + +```shell +mount -a +df -Th +``` + +![image-20240201163826924](Linux/image-20240201163826924.png) + +还原 + +```shell +xfsrestore -f sdb_dump /home/ +``` + +![image-20240201163847295](Linux/image-20240201163847295.png) + +#### 新加卷 + +Orale VirtualBox + +![image-20240130093530200](Linux/image-20240130093530200.png) + +需要选择最后一个,因为前几个是之前的备份快照 + +![image-20240130093629718](Linux/image-20240130093629718.png) + +分配大小即可,只能增加不能缩小 + +查看现有分区大小 + +```shell +df -Th +``` + +![image-20240130093817210](Linux/image-20240130093817210.png) + +查看扩容后磁盘大小 + +```shell +lsblk +``` + +![image-20240130093857754](Linux/image-20240130093857754.png) + +创建分区 + +```shell +fdisk /dev/sda +``` + +![image-20240130093939297](Linux/image-20240130093939297.png) + +![image-20240130094105250](Linux/image-20240130094105250.png) + +刷新分区并创建物理卷 + +```shell +partprobe /dev/sda +pvcreate /dev/sda4 +``` + +![image-20240130094218593](Linux/image-20240130094218593.png) + +查看卷组名称,以及卷组使用情况 + +```shell +vgdisplay +``` + +![image-20240130094244545](Linux/image-20240130094244545.png) + +将物理卷扩展到卷组 + +```shell +vgextend rl /dev/sda4 +``` + +![image-20240130094253931](Linux/image-20240130094253931.png) + +查看当前逻辑卷的空间状态 + +```shell +lvdisplay +``` + +![image-20240130094329028](Linux/image-20240130094329028.png) + +将卷组中的空闲空间扩展到根分区逻辑卷 + +```shell +lvextend -l +100%FREE /dev/rl/root +``` + +![image-20240130094418379](Linux/image-20240130094418379.png) + +刷新根分区 + +```shell +xfs_growfs /dev/rl/root +``` + +![image-20240130094442884](Linux/image-20240130094442884.png) + +扩容成功 + +![image-20240130094540550](Linux/image-20240130094540550.png) + +## 转换分区格式 + +### Microsoft 基本数据 -> Linux 文件系统 + +```shell +fdisk -l +... +Disk /dev/sdb:2.18 TiB,2400476553216 字节,4688430768 个扇区 +磁盘型号:DL2400MM0159 +单元:扇区 / 1 * 512 = 512 字节 +扇区大小(逻辑/物理):512 字节 / 4096 字节 +I/O 大小(最小/最佳):4096 字节 / 4096 字节 +磁盘标签类型:gpt +磁盘标识符:6C271C0A-2A82-416E-8A0F-A49EF6D9BA33 +设备 起点 末尾 扇区 大小 类型 +/dev/sdb1 2048 4688429055 4688427008 2.2T Microsoft 基本数据 +... +``` + +通过查看都为 Microsoft 基本数据 硬盘类型,需要转换成Linux系统能够识别的 Linux 文件系统 +先将 /dev/sdb 进行转换 + +```shell +fdisk /dev/sdb +# 输入 t 命令 代表转换分区类型 +t +# 20 代表 Linux file system +20 +# 输入 w 命令 代表改动由内存写入到硬盘中 +``` + +手动挂载硬盘 + +```shell +mkdir /sdb +mount -t ext3 /dev/sdb1 /sdb +``` + +没有报错后,可查看磁盘情况 + +```shell +df -Th +devtmpfs devtmpfs 4.0M 0 4.0M 0% /dev +tmpfs tmpfs 16G 0 16G 0% /dev/shm +tmpfs tmpfs 6.2G 19M 6.2G 1% /run +efivarfs efivarfs 304K 129K 171K 43% /sys/firmware/efi/efivars +/dev/mapper/almalinux-root xfs 382G 16G 367G 5% / +/dev/sda2 xfs 960M 416M 545M 44% /boot +/dev/sda1 vfat 200M 7.1M 193M 4% /boot/efi +tmpfs tmpfs 3.1G 100K 3.1G 1% /run/user/0 +/dev/sdb1 ext3 2.2T 72G 2.0T 4% /sdb +``` + +看到已经挂载上了,也可以访问了 + +系统重启后,挂载会失效,需再改动 /etc/fstab 文件,让其自动挂载 + +需查看要挂载的硬盘的UUID,以device方式去挂载,会导致重启后发生盘符交换问题 + +查看硬盘的UUID,通过 `blkid`查看 + +```shell +blkid /dev/sdb1 +/dev/sdb1: UUID="7d592b46-68dc-41c2-bdb3-7ee410f0bb33" TYPE="ext4" PARTUUID="cc75a3e5-bbfa-4abb-a749-241183f41510" +``` + +然后修改 /etc/fstab 文件,添加到对应硬盘前即可 + +```shell +vim /etc/fstab +# +# /etc/fstab +# Created by anaconda on Fri May 16 02:20:43 2025 +# +# Accessible filesystems, by reference, are maintained under '/dev/disk/'. +# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info. +# +# After editing this file, run 'systemctl daemon-reload' to update systemd +# units generated from this file. +# +/dev/mapper/almalinux-root / xfs defaults 0 0 +UUID=d99ec15b-05d5-4311-b5c7-497945d4805d /boot xfs defaults 0 0 +UUID=0EAB-5879 /boot/efi vfat umask=0077,shortname=winnt 0 2 +/dev/mapper/almalinux-swap none swap defaults 0 0 +# 此处追加下面硬盘信息 +UUID=7d592b46-68dc-41c2-bdb3-7ee410f0bb33 /sdb ext3 defaults 0 0 +``` + +修改 /etc/fstab 文件后,需系统重载配置,才可应用 + +```shell +systemctl daemon-reload +``` + +### ext3 升级 ext4 + +确认当前文件系统类型 + +```shell +df -Th | grep /dev/sdb1 +/dev/sdb1 ext3 2.2T 60G 2.0T 3% /sdb +``` + +卸载目标分区(⚠️ 注意:不能对正在使用的根分区操作) + +```shell +umount /dev/sdb1 +``` + +检查并修复文件系统 + +```shell +e2fsck -f /dev/sdb1 + +e2fsck 1.46.5 (30-Dec-2021) +第 1 步:检查inode、块和大小 +第 2 步:检查目录结构 +第 3 步:检查目录连接性 +第 4 步:检查引用计数 +第 5 步:检查组概要信息 +/dev/sdb1:26748/146513920 文件(15.5% 为非连续的), 24877668/586053376 块 +``` + +确保文件系统无错误。 + +将 ext3 转换为 ext4 + +```shell +tune2fs -O has_journal,extent,huge_file,flex_bg,uninit_bg,dir_nlink,extra_isize /dev/sdb1 +tune2fs 1.46.5 (30-Dec-2021) +``` + +查看转换是否成功 + +```shell +dumpe2fs -h /dev/sdb1 | grep features +dumpe2fs 1.46.5 (30-Dec-2021) +Filesystem features: has_journal ext_attr resize_inode dir_index filetype extent flex_bg sparse_super large_file huge_file uninit_bg dir_nlink extra_isize +Journal features: journal_incompat_revoke +``` + +确认输出中包含你刚刚添加的特性,例如: + +extent +huge_file +flex_bg +uninit_bg +dir_nlink +extra_isize +如果有这些关键字,说明已经成功启用了这些功能,也就是成功转换成ext4。 + +再次检查文件系统 + +```shell +e2fsck -f /dev/sdb1 +``` + +挂载并验证文件系统类型 + +```shell +mount -t ext4 /dev/sdb1 /sdb +df -Th | grep /dev/sdb1 +/dev/sdb1 ext4 2.2T 60G 2.0T 3% /sdb +``` + +修改 /etc/fstab 文件 + +```shell +/dev/mapper/almalinux-root / xfs defaults 0 0 +UUID=d99ec15b-05d5-4311-b5c7-497945d4805d /boot xfs defaults 0 0 +UUID=0EAB-5879 /boot/efi vfat umask=0077,shortname=winnt 0 2 +/dev/mapper/almalinux-swap none swap defaults 0 0 +/dev/sdb1 /sdb ext4 defaults 0 0 +``` + +保存并重载配置 + +```shell +systemctl daemon-reload +``` + +### NTFS -> ext4 + +确认目标分区 + +扎到要格式化的分区名称(例如 `/dev/sda1`)。 + +```bash +lsblk -f +``` + +卸载分区 + +如果该分区已经挂载(MOUNTPOINT 列有路径),必须先将其卸载才能格式化。 + +```bash +sudo umount /dev/sda1 +``` + +执行格式化命令 + +```bash +sudo mkfs.ext4 /dev/sda1 +``` + +验证结果 + +```bash +lsblk -f +``` + +## 挂载卷 + +### Windows 网络共享位置 + +首先创建本地的挂载目录,一般在`/mnt`下 + +这里以 `/mnt/wdshare`为例 + +```shell +mkdir -p /mnt/wdshare/ +``` + +安装`cifs-utils` + +```shell +dnf install -y cifs-utils +``` + +进行挂载 + +```shell +mount -t cifs -o username=user,password=backup //192.168.0.1/备份 /mnt/wdshare/ +``` + +以上为暂时挂载,还需要永久挂载,避免系统重启后挂载丢失 + +编辑`/etc/fstab`配置文件,在最后一行添加以下配置 + +```shell +//192.168.0.1/备份 /mnt/wdshare/ cifs username=user,password=backup 0 0 +``` + +`:wq`保存好,使用以下命令使修改生效 + +```shell +sudo systemctl daemon-reload +``` + +一键挂载上 + +```shell +sudo mount -a +``` + +### NTFS 分区 + +#### 可读可写 + +**识别NTFS分区** + +```shell +sudo parted -l +``` + +![image-20250728084352055](https://rustfs.wenyongdalucky.club:443/hexo/image-20250728084352055.png) + +**创建挂载点**:使用*mkdir*命令创建一个挂载点 + +```shell +sudo mkdir /mnt/ntfs1 +``` + +**安装依赖**:更新包仓库并安装*fuse*和*ntfs-3g* + +```shell +sudo apt update +sudo apt install fuse -y +sudo apt install ntfs-3g -y +``` + +**挂载分区**:使用*mount*命令挂载分区 + +```shell +sudo mount -t ntfs-3g /dev/sda1 /mnt/ntfs1/ +``` + +> 其中 /dev/sda1 就是由上述命令的 sudo parted -l 得来的,Disk /dev/sda: 1000GB ,这行标识出了 设备的路径,Disk Flags: 及下述表格的列 Number 则标识出了具体的 设备号,也可通过这个命令进行验证 `sudo blkid /dev/sda1` +> +> ![image-20250728085224181](https://rustfs.wenyongdalucky.club:443/hexo/image-20250728085224181.png) +> +> 可以看到 `TYPE="ntfs"`字样 + +**验证挂载**:使用*df*命令检查所有文件系统的详细信息,验证分区是否成功挂载 + +```shell +df -Th +``` + +![image-20250728084611261](https://rustfs.wenyongdalucky.club:443/hexo/image-20250728084611261.png) + +可以看到最后一行的就是刚刚挂载上的设备卷 + +这个只是临时挂载,还需要编辑`/etc/fstab`配置文件,防止系统重启后,还需再手动挂载 + +```shell +sudo vim /etc/fstab +``` + +在最后一行下面添加这一行 + +```shell +/dev/sda1 /mnt/ntfs1 fuseblk defaults 0 0 +``` + +`:wq`保存好,使用以下命令使修改生效 + +```shell +sudo systemctl daemon-reload +``` + + + +## 分配Swap + +查看分区大小 + +```shell +free -h +``` + +使用dd命令创建一个swap分区 + +```shell +dd if=/dev/zero of=/home/swap bs=1024 count=4194304 +``` + +count的值是:size(多少M)* 1024 + +格式化swap分区 + +```shell +mkswap /home/swap +``` + +把格式化后的文件分区设置为swap分区 + +```shell +swapon /home/swap +``` + +swap分区自动挂载 + +```shell +vim /etc/fstab +# G 在文件末尾加上 +/home/swap swap swap default 0 0 +``` + +关闭Swap + +```shell +swapoff /home/swap +``` + +### 修改swap使用率 + +swappiness的值的大小对如何使用swap分区是有着很大的联系的。swappiness=0的时候表示最大限度使用物理内存,然后才是 swap空间,swappiness=100的时候表示积极的使用swap分区,并且把内存上的数据及时的搬运到swap空间里面。两个极端 + +查看swappiness + +```shell +cat /proc/sys/vm/swappiness +``` + +修改swappiness值为60 + +```shell +sysctl vm.swappiness=60 +``` + +但是这只是临时性的修改,还要做一步 + +```shell +vim /etc/sysctl.conf +# 编辑这行 +vm.swappiness=60 +# 应用更改 +sysctl -p +``` + +## 升级内核 + +### centos 7.9 + +#### yum + +查看内核版本 + +```shell +uname -a +``` + +查看CentOS的版本 + +```shell +cat /etc/redhat-release +``` + +导入一个公钥 + +```shell +rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org +``` + +安装一下CentOS 7.x的ELRepo包 + +```shell +yum install -y https://www.elrepo.org/elrepo-release-7.el7.elrepo.noarch.rpm +``` + +然后执行下边命令 + +```shell +yum --enablerepo=elrepo-kernel install kernel-ml -y && +sed -i s/saved/0/g /etc/default/grub && +grub2-mkconfig -o /boot/grub2/grub.cfg +``` + +重启 + +```shell +reboot +``` + +查看内核版本 + +```shell +uname -a +``` + +升级完成 + +## 查看系统硬件信息 + +### cpu + +**lscpu** 查看的是cpu的统计信息 + +```shell +lscpu + +Architecture: x86_64 +CPU op-mode(s): 32-bit, 64-bit +Byte Order: Little Endian +CPU(s): 40 +On-line CPU(s) list: 0-39 +Thread(s) per core: 2 +Core(s) per socket: 10 +座: 2 +NUMA 节点: 2 +厂商 ID: GenuineIntel +CPU 系列: 6 +型号: 85 +型号名称: Intel(R) Xeon(R) Silver 4210 CPU @ 2.20GHz +步进: 7 +CPU MHz: 999.963 +CPU max MHz: 3200.0000 +CPU min MHz: 1000.0000 +BogoMIPS: 4400.00 +虚拟化: VT-x +L1d 缓存: 32K +L1i 缓存: 32K +L2 缓存: 1024K +L3 缓存: 14080K +NUMA 节点0 CPU: 0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38 +NUMA 节点1 CPU: 1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,33,35,37,39 +Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch epb cat_l3 cdp_l3 invpcid_single intel_ppin ssbd mba rsb_ctxsw ibrs ibpb stibp ibrs_enhanced tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm cqm mpx rdt_a avx512f avx512dq rdseed adx smap clflushopt clwb intel_pt avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local dtherm ida arat pln pts pku ospke avx512_vnni md_clear spec_ctrl intel_stibp flush_l1d arch_capabilities + +``` + +**cat /proc/cpuinfo** 可以知道每个cpu信息,如每个cpu的型号,主频等 + +```shell +cat /proc/cpuinfo + +processor : 0 +vendor_id : GenuineIntel +cpu family : 6 +model : 85 +model name : Intel(R) Xeon(R) Silver 4210 CPU @ 2.20GHz +stepping : 7 +microcode : 0x5003303 +cpu MHz : 999.963 +cache size : 14080 KB +physical id : 0 +siblings : 20 +core id : 0 +cpu cores : 10 +apicid : 0 +initial apicid : 0 +fpu : yes +fpu_exception : yes +cpuid level : 22 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch epb cat_l3 cdp_l3 invpcid_single intel_ppin ssbd mba rsb_ctxsw ibrs ibpb stibp ibrs_enhanced tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm cqm mpx rdt_a avx512f avx512dq rdseed adx smap clflushopt clwb intel_pt avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local dtherm ida arat pln pts pku ospke avx512_vnni md_clear spec_ctrl intel_stibp flush_l1d arch_capabilities +bogomips : 4400.00 +clflush size : 64 +cache_alignment : 64 +address sizes : 46 bits physical, 48 bits virtual +power management: +``` + +### 内存 + +概要查看内存情况 + +这里的单位是mb + +```shell +free -m + + total used free shared buff/cache available +Mem: 31595 14770 3182 253 13643 16150 +Swap: 65535 0 65535 + +``` + +查看内存详细使用 + +```shell +cat /proc/meminfo + +MemTotal: 32354112 kB +MemFree: 3377564 kB +MemAvailable: 16657484 kB +Buffers: 725916 kB +Cached: 12127832 kB +SwapCached: 0 kB +Active: 21031256 kB +Inactive: 5694748 kB +Active(anon): 13934208 kB +Inactive(anon): 197192 kB +Active(file): 7097048 kB +Inactive(file): 5497556 kB +Unevictable: 0 kB +Mlocked: 0 kB +SwapTotal: 67108860 kB +SwapFree: 67108860 kB +Dirty: 332 kB +Writeback: 0 kB +AnonPages: 13894944 kB +Mapped: 697472 kB +Shmem: 259160 kB +Slab: 1464576 kB +SReclaimable: 1117812 kB +SUnreclaim: 346764 kB +KernelStack: 47280 kB +PageTables: 95304 kB +NFS_Unstable: 0 kB +Bounce: 0 kB +WritebackTmp: 0 kB +CommitLimit: 83285916 kB +Committed_AS: 30011360 kB +VmallocTotal: 34359738367 kB +VmallocUsed: 499568 kB +VmallocChunk: 34342115324 kB +Percpu: 165376 kB +HardwareCorrupted: 0 kB +AnonHugePages: 9009152 kB +CmaTotal: 0 kB +CmaFree: 0 kB +HugePages_Total: 0 +HugePages_Free: 0 +HugePages_Rsvd: 0 +HugePages_Surp: 0 +Hugepagesize: 2048 kB +DirectMap4k: 510976 kB +DirectMap2M: 11681792 kB +DirectMap1G: 23068672 kB + +``` + +### 硬盘 + +查看硬盘和分区分布 + +```shell +lsblk + +NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT +sda 8:0 0 447.1G 0 disk +├─sda1 8:1 0 200M 0 part /boot/efi +├─sda2 8:2 0 1G 0 part /boot +├─sda3 8:3 0 380G 0 part / +└─sda4 8:4 0 64G 0 part [SWAP] +sdb 8:16 0 2.2T 0 disk +└─sdb1 8:17 0 2.2T 0 part /test +sdc 8:32 0 2.2T 0 disk +└─sdc1 8:33 0 2.2T 0 part /test1 +sdd 8:48 0 2.2T 0 disk +└─sdd1 8:49 0 2.2T 0 part /test2 + +``` + +查看硬盘和分区的详细信息 + +```shell +fdisk -l + +磁盘 /dev/sda:480.1 GB, 480103981056 字节,937703088 个扇区 +Units = 扇区 of 1 * 512 = 512 bytes +扇区大小(逻辑/物理):512 字节 / 4096 字节 +I/O 大小(最小/最佳):4096 字节 / 4096 字节 +磁盘标签类型:gpt +Disk identifier: F6E9395D-610B-4BB3-B289-8F6A96811113 + + +# Start End Size Type Name + 1 2048 411647 200M EFI System EFI System Partition + 2 411648 2508799 1G Microsoft basic + 3 2508800 799426559 380G Microsoft basic + 4 799426560 933644287 64G Linux swap +``` + +### 网卡 + +查看网卡硬件信息 + +```shell +lspci | grep -i 'eth' + +04:00.0 Ethernet controller: Broadcom Inc. and subsidiaries NetXtreme BCM5720 2-port Gigabit Ethernet PCIe +04:00.1 Ethernet controller: Broadcom Inc. and subsidiaries NetXtreme BCM5720 2-port Gigabit Ethernet PCIe + +``` + +查看系统的所有网络接口 + +```shell +ifconfig -a + +docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 + inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255 + inet6 fe80::42:66ff:fefe:52a2 prefixlen 64 scopeid 0x20<link> + ether 02:42:66:fe:52:a2 txqueuelen 0 (Ethernet) + RX packets 533213 bytes 84136530 (80.2 MiB) + RX errors 0 dropped 0 overruns 0 frame 0 + TX packets 451394 bytes 255184964 (243.3 MiB) + TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 + +em1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 + inet 192.168.6.20 netmask 255.255.255.0 broadcast 192.168.6.255 + inet6 fe80::bcee:f071:9cb6:5895 prefixlen 64 scopeid 0x20<link> + ether 2c:ea:7f:a9:fc:76 txqueuelen 1000 (Ethernet) + RX packets 4188110 bytes 589201250 (561.9 MiB) + RX errors 0 dropped 245827 overruns 0 frame 0 + TX packets 3750302 bytes 3040465610 (2.8 GiB) + TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 + device interrupt 17 + +em2: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500 + ether 2c:ea:7f:a9:fc:77 txqueuelen 1000 (Ethernet) + RX packets 0 bytes 0 (0.0 B) + RX errors 0 dropped 0 overruns 0 frame 0 + TX packets 0 bytes 0 (0.0 B) + TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 + device interrupt 18 + +lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536 + inet 127.0.0.1 netmask 255.0.0.0 + inet6 ::1 prefixlen 128 scopeid 0x10<host> + loop txqueuelen 1000 (Local Loopback) + RX packets 57812 bytes 1222825457 (1.1 GiB) + RX errors 0 dropped 0 overruns 0 frame 0 + TX packets 57812 bytes 1222825457 (1.1 GiB) + TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 + +veth466b258: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 + inet6 fe80::f416:2bff:feda:768a prefixlen 64 scopeid 0x20<link> + ether f6:16:2b:da:76:8a txqueuelen 0 (Ethernet) + RX packets 82533 bytes 24308568 (23.1 MiB) + RX errors 0 dropped 0 overruns 0 frame 0 + TX packets 164361 bytes 88237154 (84.1 MiB) + TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 + +veth52ce7e6: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 + inet6 fe80::4806:98ff:fe5f:bb2d prefixlen 64 scopeid 0x20<link> + ether 4a:06:98:5f:bb:2d txqueuelen 0 (Ethernet) + RX packets 450680 bytes 67292944 (64.1 MiB) + RX errors 0 dropped 0 overruns 0 frame 0 + TX packets 287123 bytes 166954717 (159.2 MiB) + TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 + +veth675fcbf: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 + inet6 fe80::f4ac:10ff:fef5:5d60 prefixlen 64 scopeid 0x20<link> + ether f6:ac:10:f5:5d:60 txqueuelen 0 (Ethernet) + RX packets 0 bytes 0 (0.0 B) + RX errors 0 dropped 0 overruns 0 frame 0 + TX packets 107 bytes 9629 (9.4 KiB) + TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 + +virbr0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500 + inet 192.168.122.1 netmask 255.255.255.0 broadcast 192.168.122.255 + ether 52:54:00:a4:7b:fe txqueuelen 1000 (Ethernet) + RX packets 0 bytes 0 (0.0 B) + RX errors 0 dropped 0 overruns 0 frame 0 + TX packets 0 bytes 0 (0.0 B) + TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 + +virbr0-nic: flags=4098<BROADCAST,MULTICAST> mtu 1500 + ether 52:54:00:a4:7b:fe txqueuelen 1000 (Ethernet) + RX packets 0 bytes 0 (0.0 B) + RX errors 0 dropped 0 overruns 0 frame 0 + TX packets 0 bytes 0 (0.0 B) + TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 + +``` + +或者是 + +```shell +ip link show + +1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 + link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 +2: em1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000 + link/ether 2c:ea:7f:a9:fc:76 brd ff:ff:ff:ff:ff:ff +3: em2: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc mq state DOWN mode DEFAULT group default qlen 1000 + link/ether 2c:ea:7f:a9:fc:77 brd ff:ff:ff:ff:ff:ff +4: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default + link/ether 02:42:66:fe:52:a2 brd ff:ff:ff:ff:ff:ff +6: veth466b258@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default + link/ether f6:16:2b:da:76:8a brd ff:ff:ff:ff:ff:ff link-netnsid 1 +10: veth675fcbf@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default + link/ether f6:ac:10:f5:5d:60 brd ff:ff:ff:ff:ff:ff link-netnsid 0 +11: virbr0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default qlen 1000 + link/ether 52:54:00:a4:7b:fe brd ff:ff:ff:ff:ff:ff +12: virbr0-nic: <BROADCAST,MULTICAST> mtu 1500 qdisc pfifo_fast master virbr0 state DOWN mode DEFAULT group default qlen 1000 + link/ether 52:54:00:a4:7b:fe brd ff:ff:ff:ff:ff:ff +14: veth52ce7e6@if13: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default + link/ether 4a:06:98:5f:bb:2d brd ff:ff:ff:ff:ff:ff link-netnsid 2 + +``` + +或者 + +```shell +cat /proc/net/dev + +Inter-| Receive | Transmit + face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed +veth466b258: 24315353 82556 0 0 0 0 0 0 88261902 164407 0 0 0 0 0 0 + lo: 1222873114 57968 0 0 0 0 0 0 1222873114 57968 0 0 0 0 0 0 +virbr0-nic: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +virbr0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +veth675fcbf: 0 0 0 0 0 0 0 0 9629 107 0 0 0 0 0 0 + em1: 589404500 4189635 0 245895 0 0 0 966587 3040611778 3751409 0 0 0 0 0 0 + em2: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +veth52ce7e6: 67310666 450811 0 0 0 0 0 0 167000201 287207 0 0 0 0 0 0 +docker0: 84158881 533367 0 0 0 0 0 0 255255196 451524 0 0 0 0 0 0 + +``` + +如果要查看某个网络接口的详细信息,例如em1的详细参数和指标 + +```shell +ethtool em1 + +Settings for em1: + Supported ports: [ TP ] + Supported link modes: 10baseT/Half 10baseT/Full + 100baseT/Half 100baseT/Full + 1000baseT/Half 1000baseT/Full + Supported pause frame use: No + Supports auto-negotiation: Yes + Supported FEC modes: Not reported + Advertised link modes: 10baseT/Half 10baseT/Full + 100baseT/Half 100baseT/Full + 1000baseT/Half 1000baseT/Full + Advertised pause frame use: Symmetric + Advertised auto-negotiation: Yes + Advertised FEC modes: Not reported + Link partner advertised link modes: 10baseT/Half 10baseT/Full + 100baseT/Half 100baseT/Full + Link partner advertised pause frame use: Symmetric Receive-only + Link partner advertised auto-negotiation: Yes + Link partner advertised FEC modes: Not reported + Speed: 100Mb/s + Duplex: Full + Port: Twisted Pair + PHYAD: 1 + Transceiver: internal + Auto-negotiation: on + MDI-X: off + Supports Wake-on: g + Wake-on: d + Current message level: 0x000000ff (255) + drv probe link timer ifdown ifup rx_err tx_err + Link detected: yes + +``` + +### pci + +查看pci信息,即主板所有硬件槽信息 + +```shell +lspci + +00:00.0 Host bridge: Intel Corporation Sky Lake-E DMI3 Registers (rev 07) +00:05.0 System peripheral: Intel Corporation Sky Lake-E MM/Vt-d Configuration Registers (rev 07) +00:05.2 System peripheral: Intel Corporation Sky Lake-E RAS (rev 07) +00:05.4 PIC: Intel Corporation Sky Lake-E IOAPIC (rev 07) +00:08.0 System peripheral: Intel Corporation Sky Lake-E Ubox Registers (rev 07) +00:08.1 Performance counters: Intel Corporation Sky Lake-E Ubox Registers (rev 07) +00:08.2 System peripheral: Intel Corporation Sky Lake-E Ubox Registers (rev 07) +00:11.0 Unassigned class [ff00]: Intel Corporation C620 Series Chipset Family MROM 0 (rev 09) +00:11.5 SATA controller: Intel Corporation C620 Series Chipset Family SSATA Controller [AHCI mode] (rev 09) +00:14.0 USB controller: Intel Corporation C620 Series Chipset Family USB 3.0 xHCI Controller (rev 09) +00:14.2 Signal processing controller: Intel Corporation C620 Series Chipset Family Thermal Subsystem (rev 09) +00:16.0 Communication controller: Intel Corporation C620 Series Chipset Family MEI Controller #1 (rev 09) +00:16.1 Communication controller: Intel Corporation C620 Series Chipset Family MEI Controller #2 (rev 09) +00:16.4 Communication controller: Intel Corporation C620 Series Chipset Family MEI Controller #3 (rev 09) +00:17.0 SATA controller: Intel Corporation C620 Series Chipset Family SATA Controller [AHCI mode] (rev 09) +00:1c.0 PCI bridge: Intel Corporation C620 Series Chipset Family PCI Express Root Port #1 (rev f9) +00:1c.4 PCI bridge: Intel Corporation C620 Series Chipset Family PCI Express Root Port #5 (rev f9) +00:1c.5 PCI bridge: Intel Corporation C620 Series Chipset Family PCI Express Root Port #6 (rev f9) +00:1f.0 ISA bridge: Intel Corporation C621 Series Chipset LPC/eSPI Controller (rev 09) +00:1f.2 Memory controller: Intel Corporation C620 Series Chipset Family Power Management Controller (rev 09) +00:1f.4 SMBus: Intel Corporation C620 Series Chipset Family SMBus (rev 09) +00:1f.5 Serial bus controller [0c80]: Intel Corporation C620 Series Chipset Family SPI Controller (rev 09) +02:00.0 PCI bridge: PLDA PCI Express Bridge (rev 02) +03:00.0 VGA compatible controller: Matrox Electronics Systems Ltd. Integrated Matrox G200eW3 Graphics Controller (rev 04) +04:00.0 Ethernet controller: Broadcom Inc. and subsidiaries NetXtreme BCM5720 2-port Gigabit Ethernet PCIe +04:00.1 Ethernet controller: Broadcom Inc. and subsidiaries NetXtreme BCM5720 2-port Gigabit Ethernet PCIe +17:02.0 PCI bridge: Intel Corporation Sky Lake-E PCI Express Root Port C (rev 07) +17:05.0 System peripheral: Intel Corporation Sky Lake-E VT-d (rev 07) +17:05.2 System peripheral: Intel Corporation Sky Lake-E RAS Configuration Registers (rev 07) +17:05.4 PIC: Intel Corporation Sky Lake-E IOxAPIC Configuration Registers (rev 07) +17:08.0 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +17:08.1 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +17:08.2 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +17:08.3 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +17:08.4 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +17:08.5 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +17:08.6 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +17:08.7 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +17:09.0 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +17:09.1 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +17:0e.0 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +17:0e.1 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +17:0e.2 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +17:0e.3 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +17:0e.4 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +17:0e.5 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +17:0e.6 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +17:0e.7 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +17:0f.0 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +17:0f.1 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +17:1d.0 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +17:1d.1 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +17:1d.2 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +17:1d.3 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +17:1e.0 System peripheral: Intel Corporation Sky Lake-E PCU Registers (rev 07) +17:1e.1 System peripheral: Intel Corporation Sky Lake-E PCU Registers (rev 07) +17:1e.2 System peripheral: Intel Corporation Sky Lake-E PCU Registers (rev 07) +17:1e.3 System peripheral: Intel Corporation Sky Lake-E PCU Registers (rev 07) +17:1e.4 System peripheral: Intel Corporation Sky Lake-E PCU Registers (rev 07) +17:1e.5 System peripheral: Intel Corporation Sky Lake-E PCU Registers (rev 07) +17:1e.6 System peripheral: Intel Corporation Sky Lake-E PCU Registers (rev 07) +18:00.0 RAID bus controller: Broadcom / LSI MegaRAID SAS-3 3108 [Invader] (rev 02) +3a:05.0 System peripheral: Intel Corporation Sky Lake-E VT-d (rev 07) +3a:05.2 System peripheral: Intel Corporation Sky Lake-E RAS Configuration Registers (rev 07) +3a:05.4 PIC: Intel Corporation Sky Lake-E IOxAPIC Configuration Registers (rev 07) +3a:08.0 System peripheral: Intel Corporation Sky Lake-E Integrated Memory Controller (rev 07) +3a:09.0 System peripheral: Intel Corporation Sky Lake-E Integrated Memory Controller (rev 07) +3a:0a.0 System peripheral: Intel Corporation Sky Lake-E Integrated Memory Controller (rev 07) +3a:0a.1 System peripheral: Intel Corporation Sky Lake-E Integrated Memory Controller (rev 07) +3a:0a.2 System peripheral: Intel Corporation Sky Lake-E Integrated Memory Controller (rev 07) +3a:0a.3 System peripheral: Intel Corporation Sky Lake-E Integrated Memory Controller (rev 07) +3a:0a.4 System peripheral: Intel Corporation Sky Lake-E Integrated Memory Controller (rev 07) +3a:0a.5 System peripheral: Intel Corporation Sky Lake-E LM Channel 1 (rev 07) +3a:0a.6 System peripheral: Intel Corporation Sky Lake-E LMS Channel 1 (rev 07) +3a:0a.7 System peripheral: Intel Corporation Sky Lake-E LMDP Channel 1 (rev 07) +3a:0b.0 System peripheral: Intel Corporation Sky Lake-E DECS Channel 2 (rev 07) +3a:0b.1 System peripheral: Intel Corporation Sky Lake-E LM Channel 2 (rev 07) +3a:0b.2 System peripheral: Intel Corporation Sky Lake-E LMS Channel 2 (rev 07) +3a:0b.3 System peripheral: Intel Corporation Sky Lake-E LMDP Channel 2 (rev 07) +3a:0c.0 System peripheral: Intel Corporation Sky Lake-E Integrated Memory Controller (rev 07) +3a:0c.1 System peripheral: Intel Corporation Sky Lake-E Integrated Memory Controller (rev 07) +3a:0c.2 System peripheral: Intel Corporation Sky Lake-E Integrated Memory Controller (rev 07) +3a:0c.3 System peripheral: Intel Corporation Sky Lake-E Integrated Memory Controller (rev 07) +3a:0c.4 System peripheral: Intel Corporation Sky Lake-E Integrated Memory Controller (rev 07) +3a:0c.5 System peripheral: Intel Corporation Sky Lake-E LM Channel 1 (rev 07) +3a:0c.6 System peripheral: Intel Corporation Sky Lake-E LMS Channel 1 (rev 07) +3a:0c.7 System peripheral: Intel Corporation Sky Lake-E LMDP Channel 1 (rev 07) +3a:0d.0 System peripheral: Intel Corporation Sky Lake-E DECS Channel 2 (rev 07) +3a:0d.1 System peripheral: Intel Corporation Sky Lake-E LM Channel 2 (rev 07) +3a:0d.2 System peripheral: Intel Corporation Sky Lake-E LMS Channel 2 (rev 07) +3a:0d.3 System peripheral: Intel Corporation Sky Lake-E LMDP Channel 2 (rev 07) +5d:05.0 System peripheral: Intel Corporation Sky Lake-E VT-d (rev 07) +5d:05.2 System peripheral: Intel Corporation Sky Lake-E RAS Configuration Registers (rev 07) +5d:05.4 PIC: Intel Corporation Sky Lake-E IOxAPIC Configuration Registers (rev 07) +5d:0e.0 Performance counters: Intel Corporation Sky Lake-E KTI 0 (rev 07) +5d:0e.1 System peripheral: Intel Corporation Sky Lake-E UPI Registers (rev 07) +5d:0f.0 Performance counters: Intel Corporation Sky Lake-E KTI 0 (rev 07) +5d:0f.1 System peripheral: Intel Corporation Sky Lake-E UPI Registers (rev 07) +5d:12.0 Performance counters: Intel Corporation Sky Lake-E M3KTI Registers (rev 07) +5d:12.1 Performance counters: Intel Corporation Sky Lake-E M3KTI Registers (rev 07) +5d:12.2 System peripheral: Intel Corporation Sky Lake-E M3KTI Registers (rev 07) +5d:15.0 System peripheral: Intel Corporation Sky Lake-E M2PCI Registers (rev 07) +5d:15.1 Performance counters: Intel Corporation Sky Lake-E DDRIO Registers (rev 07) +5d:16.0 System peripheral: Intel Corporation Sky Lake-E M2PCI Registers (rev 07) +5d:16.1 Performance counters: Intel Corporation Sky Lake-E DDRIO Registers (rev 07) +5d:16.4 System peripheral: Intel Corporation Sky Lake-E M2PCI Registers (rev 07) +5d:16.5 Performance counters: Intel Corporation Sky Lake-E DDRIO Registers (rev 07) +80:05.0 System peripheral: Intel Corporation Sky Lake-E MM/Vt-d Configuration Registers (rev 07) +80:05.2 System peripheral: Intel Corporation Sky Lake-E RAS (rev 07) +80:05.4 PIC: Intel Corporation Sky Lake-E IOAPIC (rev 07) +80:08.0 System peripheral: Intel Corporation Sky Lake-E Ubox Registers (rev 07) +80:08.1 Performance counters: Intel Corporation Sky Lake-E Ubox Registers (rev 07) +80:08.2 System peripheral: Intel Corporation Sky Lake-E Ubox Registers (rev 07) +85:05.0 System peripheral: Intel Corporation Sky Lake-E VT-d (rev 07) +85:05.2 System peripheral: Intel Corporation Sky Lake-E RAS Configuration Registers (rev 07) +85:05.4 PIC: Intel Corporation Sky Lake-E IOxAPIC Configuration Registers (rev 07) +85:08.0 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +85:08.1 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +85:08.2 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +85:08.3 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +85:08.4 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +85:08.5 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +85:08.6 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +85:08.7 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +85:09.0 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +85:09.1 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +85:0e.0 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +85:0e.1 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +85:0e.2 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +85:0e.3 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +85:0e.4 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +85:0e.5 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +85:0e.6 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +85:0e.7 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +85:0f.0 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +85:0f.1 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +85:1d.0 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +85:1d.1 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +85:1d.2 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +85:1d.3 System peripheral: Intel Corporation Sky Lake-E CHA Registers (rev 07) +85:1e.0 System peripheral: Intel Corporation Sky Lake-E PCU Registers (rev 07) +85:1e.1 System peripheral: Intel Corporation Sky Lake-E PCU Registers (rev 07) +85:1e.2 System peripheral: Intel Corporation Sky Lake-E PCU Registers (rev 07) +85:1e.3 System peripheral: Intel Corporation Sky Lake-E PCU Registers (rev 07) +85:1e.4 System peripheral: Intel Corporation Sky Lake-E PCU Registers (rev 07) +85:1e.5 System peripheral: Intel Corporation Sky Lake-E PCU Registers (rev 07) +85:1e.6 System peripheral: Intel Corporation Sky Lake-E PCU Registers (rev 07) +ae:05.0 System peripheral: Intel Corporation Sky Lake-E VT-d (rev 07) +ae:05.2 System peripheral: Intel Corporation Sky Lake-E RAS Configuration Registers (rev 07) +ae:05.4 PIC: Intel Corporation Sky Lake-E IOxAPIC Configuration Registers (rev 07) +ae:08.0 System peripheral: Intel Corporation Sky Lake-E Integrated Memory Controller (rev 07) +ae:09.0 System peripheral: Intel Corporation Sky Lake-E Integrated Memory Controller (rev 07) +ae:0a.0 System peripheral: Intel Corporation Sky Lake-E Integrated Memory Controller (rev 07) +ae:0a.1 System peripheral: Intel Corporation Sky Lake-E Integrated Memory Controller (rev 07) +ae:0a.2 System peripheral: Intel Corporation Sky Lake-E Integrated Memory Controller (rev 07) +ae:0a.3 System peripheral: Intel Corporation Sky Lake-E Integrated Memory Controller (rev 07) +ae:0a.4 System peripheral: Intel Corporation Sky Lake-E Integrated Memory Controller (rev 07) +ae:0a.5 System peripheral: Intel Corporation Sky Lake-E LM Channel 1 (rev 07) +ae:0a.6 System peripheral: Intel Corporation Sky Lake-E LMS Channel 1 (rev 07) +ae:0a.7 System peripheral: Intel Corporation Sky Lake-E LMDP Channel 1 (rev 07) +ae:0b.0 System peripheral: Intel Corporation Sky Lake-E DECS Channel 2 (rev 07) +ae:0b.1 System peripheral: Intel Corporation Sky Lake-E LM Channel 2 (rev 07) +ae:0b.2 System peripheral: Intel Corporation Sky Lake-E LMS Channel 2 (rev 07) +ae:0b.3 System peripheral: Intel Corporation Sky Lake-E LMDP Channel 2 (rev 07) +ae:0c.0 System peripheral: Intel Corporation Sky Lake-E Integrated Memory Controller (rev 07) +ae:0c.1 System peripheral: Intel Corporation Sky Lake-E Integrated Memory Controller (rev 07) +ae:0c.2 System peripheral: Intel Corporation Sky Lake-E Integrated Memory Controller (rev 07) +ae:0c.3 System peripheral: Intel Corporation Sky Lake-E Integrated Memory Controller (rev 07) +ae:0c.4 System peripheral: Intel Corporation Sky Lake-E Integrated Memory Controller (rev 07) +ae:0c.5 System peripheral: Intel Corporation Sky Lake-E LM Channel 1 (rev 07) +ae:0c.6 System peripheral: Intel Corporation Sky Lake-E LMS Channel 1 (rev 07) +ae:0c.7 System peripheral: Intel Corporation Sky Lake-E LMDP Channel 1 (rev 07) +ae:0d.0 System peripheral: Intel Corporation Sky Lake-E DECS Channel 2 (rev 07) +ae:0d.1 System peripheral: Intel Corporation Sky Lake-E LM Channel 2 (rev 07) +ae:0d.2 System peripheral: Intel Corporation Sky Lake-E LMS Channel 2 (rev 07) +ae:0d.3 System peripheral: Intel Corporation Sky Lake-E LMDP Channel 2 (rev 07) +d7:05.0 System peripheral: Intel Corporation Sky Lake-E VT-d (rev 07) +d7:05.2 System peripheral: Intel Corporation Sky Lake-E RAS Configuration Registers (rev 07) +d7:05.4 PIC: Intel Corporation Sky Lake-E IOxAPIC Configuration Registers (rev 07) +d7:0e.0 Performance counters: Intel Corporation Sky Lake-E KTI 0 (rev 07) +d7:0e.1 System peripheral: Intel Corporation Sky Lake-E UPI Registers (rev 07) +d7:0f.0 Performance counters: Intel Corporation Sky Lake-E KTI 0 (rev 07) +d7:0f.1 System peripheral: Intel Corporation Sky Lake-E UPI Registers (rev 07) +d7:12.0 Performance counters: Intel Corporation Sky Lake-E M3KTI Registers (rev 07) +d7:12.1 Performance counters: Intel Corporation Sky Lake-E M3KTI Registers (rev 07) +d7:12.2 System peripheral: Intel Corporation Sky Lake-E M3KTI Registers (rev 07) +d7:15.0 System peripheral: Intel Corporation Sky Lake-E M2PCI Registers (rev 07) +d7:15.1 Performance counters: Intel Corporation Sky Lake-E DDRIO Registers (rev 07) +d7:16.0 System peripheral: Intel Corporation Sky Lake-E M2PCI Registers (rev 07) +d7:16.1 Performance counters: Intel Corporation Sky Lake-E DDRIO Registers (rev 07) +d7:16.4 System peripheral: Intel Corporation Sky Lake-E M2PCI Registers (rev 07) +d7:16.5 Performance counters: Intel Corporation Sky Lake-E DDRIO Registers (rev 07) + +``` + +查看更信息的信息 + +```shell +lspci -v 或者 lspci -vv +``` + +如果要查看设备树 + +```shell +lspci -t +``` + +### usb + +查看usb信息 + +```shell +lsusb + +Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub +Bus 001 Device 004: ID 1604:10c0 Tascam Dell Integrated Hub +Bus 001 Device 003: ID 1604:10c0 Tascam Dell Integrated Hub +Bus 001 Device 002: ID 1604:10c0 Tascam Dell Integrated Hub +Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub + +``` + +lsusb -t 查看系统中的usb拓扑 + +```she +lsusb -t + +/: Bus 02.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/10p, 5000M +/: Bus 01.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/16p, 480M + |__ Port 14: Dev 2, If 0, Class=Hub, Driver=hub/4p, 480M + |__ Port 1: Dev 3, If 0, Class=Hub, Driver=hub/4p, 480M + |__ Port 4: Dev 4, If 0, Class=Hub, Driver=hub/4p, 480M + +``` + +lsusb -v 查看系统中usb设备的详细信息 + +```shell +lsusb -v +``` + +## 安装新字体 + +### 查看已安装的字体 + +```shell +fc-list +``` + +### 下载字体文件 + +```shell +git clone https://gitee.com/mirrors/nerd-fonts.git --depth 1 +``` + +### 导航到 `/usr/share/fonts` 目录 + +如果没有,就创建一个 + +```shell +cd /usr/share/fonts +#or +sudo mkdir /usr/share/fonts +``` + +### 复制字体 + +使用以下命令将下载好的字体文件(ttf、otf 等)复制到新创建的 `fonts` 目录中: + +```shell +sudo cp -r ~/nerd-fonts/patched-fonts/Hack /usr/share/fonts +``` + +### 更新字体缓存 + +```shell +sudo fc-cache -f -v +``` + +### 为特定用户安装字体 + +#### 导航到`.fonts`目录 + +如果没有,就创建一个 + +```shell +mkdir ~/.fonts +``` + +#### 复制字体 + +使用以下命令将下载好的字体文件(ttf、otf等)复制到新创建的 `.fonts` 目录中: + +```shell +cp -r ~/nerd-fonts/patched-fonts/Hack ~/.fonts +``` + +#### 更新字体缓存 + +为用户特定的字体更新字体缓存: + +```shell +fc-cache -f -v +``` + +字体安装完成后,同样可以通过 `fc-list` 命令验证新字体是否成功安装。 + +## Windows Linux子系统 + +### WSL2 + +打开 Windows Terminal PowerShell + +#### 安装 + +进入控制面板中的 程序和功能 页面 + +用组合键 Win + R 启动运行窗口 `appwiz.cpl` 回车 + +启用或关闭 Windows 功能,勾选适用于 Linux 的 Windows 子系统 + +![image-20250728082044688](https://rustfs.wenyongdalucky.club:443/hexo/image-20250728082044688.png) + +也可解决安装发行版时的报错问题 + +由于未安装所需的特性,无法启动操作。 +错误代码: Wsl/InstallDistro/Service/RegisterDistro/CreateVm/HCS/HCS_E_SERVICE_NOT_AVAILABLE + +```powershell +wsl --install +``` + +微软官方文档 [安装 WSL | Microsoft Docs](https://docs.microsoft.com/zh-cn/windows/wsl/install) + +默认安装Ubuntu 20.04 LTS版 + +更改默认安装的Linux发行版 + +```powershell +wsl --install -d <Distribution Name> +``` + +/mnt目录下是Windows系统的挂载盘,可直接访问Windows磁盘文件 + +#### 迁移 + +```powershell +wsl --manage Ubuntu-24.04 --move d:\ubuntu +``` + +#### 导出 + +**查看当前 WSL 分发版** + +```powershell +wsl -l +``` + +输出示例: + +```powershell +适用于 Linux 的 Windows 子系统分发: +archlinux (默认值) +``` + +**停止运行中的 WSL** + +```powershell +wsl --terminate archlinux +``` + +**导出镜像** + +使用 *wsl --export* 命令将分发版导出为 *.tar* 文件: + +```powershell +wsl --export archlinux E:\Backup\archlinux.tar +``` + +#### 导入 + + + +#### 通过FinalShell连接WSL2 + +##### 方式1 + +1. 需要先删除ssh,再安装ssh + +```shell +apt-get remove --purge openssh-server #先删ssh +apt-get install openssh-server #再安装ssh +rm /etc/ssh/ssh_config +service ssh --full-restart #重启ssh服务 +``` + +2. 修改配置文件 + +```shell +vim /etc/ssh/sshd_config + +Port 6666 # 指定连接端口 6666 +ListenAddress 0.0.0.0 # 指定连接的IP +PasswordAuthentication yes # 开启密码认证 +PermitRootLogin yes # 开启root用户登录 + +``` + +3. 重启ssh(每次重启wsl都要执行该语句) + +```shell +service ssh --full-restart +``` + +4. 重新生成host key + +```shell +dpkg-reconfigure openssh-serve +``` + +FinalShell就可以连接WSL2了 + +##### 方式2 + +(1)查看wsl的地址 + +- 安装`ifconfig`工具 + +``` +apt install net-tools +``` + +- 查看IP地址,红框位置为wsl地址 + +``` +ifconfig +``` + +![image-20250310130413226](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20250310130413226.png) + +(2)将端口转发到wsl,在Power Shell下执行命令,将[IP]和[PORT] 替换为wsl的IP(对应图片中红框标注的)和端口(对应sshd中设置)。 + +``` +netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=2222 connectaddress=[IP] connectport=[PORT] +``` + +#### 启用systemctl + +进入当前发行版 + +编辑 /etc/wsl.conf + +```shell +vim /etc/wsl.conf +# 内容如下 +[boot] +systemd=true +``` + +重启WSL + +```powershell +wsl --shutdown +``` + +#### 取消密码复杂度及长度限制 + +```shel +vim /etc/pam.d/system-auth +password requisite pam_pwquality.so try_first_pass local_users_only retry=3 authtok_type= ​​minlen=6 ucredit=1 lcredit=1 ocredit=1 dcredit=1​​ +``` + +如下图: + +![image-20250310125937362](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20250310125937362.png) + +#### WSL玄学bug之SSH连接找不到nvidia-smi + +本地正常,但是通过SSH连接WSL,执行nvidia-smi找不到 + +##### 解决方式 + +在.bashrc中加入 + +```shell +export PATH=/usr/lib/wsl/lib:$PATH +``` + +> 参考:[https://github.com/microsoft/WSL/issues/8794](https://github.com/microsoft/WSL/issues/8794) + +## 安装 7zip + +```shell +# 更新系统数据库 +sudo dnf update -y +# 启用 Epel repository +sudo dnf install epel-release +# 安装 7-Zip +sudo dnf install p7zip p7zip-plugins +# 检验是否安装上 +7z + +# 使用 +# 创建压缩文件 命令中的选项a用于压缩 +7z a data.7z data.txt +# 显示每个存档文件的详细信息列表 +7z l data.7z +# 解压缩 +# 注意 -o 用来指定解压缩文件存放目录,-o 后是没有空格的,直接接目录 +7z x data.7z -r -o./data +``` + + + +## 安装 Nginx + +```shell +tar -zxvf nginx-1.21.4.tar.gz +cd nginx-1.21.4/ +./configure +make +make install +``` + +AlmaLinux 下安装 + +```shell +# 确保软件是最新的 +sudo dnf clean all +sudo dnf update +sudo dnf groupinstall "Development Tools" +# 安装 +sudo dnf install nginx + +sudo systemctl restart nginx +sudo systemctl status nginx +sudo systemctl enable nginx + +sudo firewall-cmd --permanent --add-service=http +sudo firewall-cmd --permanent --add-service=https +sudo firewall-cmd --reload +``` + +- `/etc/nginx`: 包含所有 Nginx 配置文件的主目录。 +- `/etc/nginx/nginx.conf`: 主要的 Nginx 配置文件。 +- `/etc/nginx/sites-available`:定义各个网站的目录。请记住,Nginx 不会使用在此目录中找到的配置文件,除非它们链接到该目录。`/etc/nginx/sites-enabled` +- `/etc/nginx/sites-enabled`: Nginx 积极服务的网站列表。 +- `/var/log/nginx`: Nginx日志目录 + +## 安装 Redis + +### dnf 方式 + +在安装Redis之前,运行下面的命令来重建软件包缓存并获得最新版本的软件包信息。 + +```shell +sudo dnf makecache +``` + +现在,运行下面的dnf命令来安装Redis。在提示时输入y,然后按ENTER键继续。 + +```shell +sudo dnf install redis +``` + +Redis安装完毕后,运行下面的systemctl命令,启动并启用Redis服务。 + +```shell +sudo systemctl start redis +sudo systemctl enable redis +``` + +最后,使用下面的命令验证Redis的服务状态。 + +```shell +sudo systemctl is-enabled redis +sudo systemctl status redis +redis-server +``` + +下面的输出确认Redis正在运行并被启用,这意味着它将在系统启动时自动运行。 + +### source 方式 + +```shell +wget https://download.redis.io/releases/redis-6.2.14.tar.gz?_gl=1*5gj06y*_gcl_au*NjUxNzAyNzkwLjE3MjQwNDc0OTc. + +tar -zxvf redis-6.2.14.tar.gz +cd redis-6.2.14.tar.gz +# 编译并且安装,默认安装在/usr/local/bin/ +make && make install +# 编译并且指定安装目录 +make && make PREFIX=/test/www/server/redis-6.2.14 install +# 测试 +make test +``` + + + +### 配置Redis + +使用下面的vim编辑器命令打开Redis配置文件"/etc/redis.conf"。 + +``` +sudo vim /etc/redis.conf +``` + +### Redis-CLI + +```shell +redis-cli +auth <password> + +``` + +## 安装.Net 6 SDK + +```shell +sudo rpm -Uvh https://packages.microsoft.com/config/centos/7/packages-microsoft-prod.rpm +sudo yum install dotnet-sdk-6.0 +dotnet --info +``` + +## 安装Node.js + +从官网下载Node.js安装包 + +地址:[Download | Node.js (nodejs.org)](https://nodejs.org/en/download/) + +![image-20221122084035020](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221122084035020.png) + +上传 + +```shell +# 解压 +tar -xvf node-v18.12.1-linux-x64.tar.xz +# 重命名为nodejs +mv node-v18.12.1-linux-x64 nodejs +# 移动到指定目录 +mv nodejs /usr/local +# 软链接方式让npm和node命令全局生效 +ln -s /usr/local/nodejs/bin/npm /usr/local/bin/ +ln -s /usr/local/nodejs/bin/node /usr/local/bin/ +# or 加入环境变量 +vim /etc/profile +# 加入下面行 +export PATH=$PATH:/usr/local/nodejs/bin +# 查看nodejs是否安装成功 +node -v +npm -v +# 如果报错 +wget https://ftp.gnu.org/gnu/glibc/glibc-2.17.tar.gz +tar -zxvf glibc-2.17.tar.gz +cd glibc-2.17 +mkdir build +cd build + +../configure --prefix=/usr --disable-profile --enable-add-ons --with-headers=/usr/include --with-binutils=/usr/bin #安装 make && make install + +docker run -itd --name nodejs -v /usr/local/bin/npm:/usr/local/bin/npm n +``` + +### AlmaLinux 安装 + +```shell +dnf update -y +dnf install nodejs -y +``` + +### 编译好的包安装(Prebuilt Binaries) + +```shell +tar -xf node-v16.20.2-linux-x64.tar.xz +mv node-v16.20.2-linux-x64 /var/lib +ln -s /var/lib/node-v16.20.2-linux-x64/bin/node /usr/bin/node +ln -s /var/lib/node-v16.20.2-linux-x64/bin/npm /usr/bin/npm +ln -s /var/lib/node-v16.20.2-linux-x64/bin/npx /usr/bin/npx +``` + +## 安装宝塔面板 + + + +## 安装 Neofetch + +```shell +dnf install epel-release +dnf install neofetch +neofetch +``` + +## 安装 Fastfetch + +```bash +dnf update -y +wget https://github.com/fastfetch-cli/fastfetch/releases/download/2.49.0/fastfetch-linux-amd64.rpm -O fastfetch.rpm +dnf install -y fastfetch.rpm +``` + + + +## 安装 Screenfetch + +```shell +dnf install git +git clone https://github.com/KittyKatt/screenFetch.git +cp screenFetch/screenfetch-dev /usr/bin/screenfetch +chmod +x /usr/bin/screenfetch +screenfetch +``` + +## 安装 Edge 和 Chrome + +### Edge + +更新源 + +```shell +sudo dnf update -y +#sudo dnf install dnf-utils -y +``` + +添加Edge源 + +```shell +sudo dnf config-manager --add-repo https://packages.microsoft.com/yumrepos/edgexxxxxxxxxx2 1sudo dnf confsudo dnf config-manager --add-repo https://packages.microsoft.com/yumrepos/edge2 +``` + +再次更新源 + +```shell +sudo dnf update -y +``` + +安装Edge + +```shell +sudo dnf install microsoft-edge-stable -y +``` + +### Chrome + +下载chrome安装文件 + +```shell +wget https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm +``` + +安装chrome + +```shell +sudo dnf install ./google-chrome-stable_current_x86_64.rpm -y +``` + +## 安装 Supervisor + +### 安装 + +```shell +sudo dnf update -y +sudo dnf install epel-release -y +sudo dnf install supervisor -y +``` + +### 配置 + +```shell +sudo vim /etc/supervisord.conf +# 开启web服务管理界面 +# 修改port中的ip为0.0.0.0,以允许任何ip访问 +# 修改用户名密码 +# 去掉行首的 ; 以使配置生效 +[inet_http_server] ; inet (TCP) server disabled by default +port=0.0.0.0:9001 ; (ip_address:port specifier, *:port for all iface) +username=user ; (default is no username (open server)) +password=123 ; (default is no password (open server)) +# 修改包含子配置文件,文件类型为.conf,默认为.ini +[include] +files = supervisord.d/*.conf +``` + +### 常用命令 + +```shell +# 启动 supervisord +sudo systemctl start supervisord +# 开机启动 supervisord +sudo systemctl enable supervisord +# 检查是否开机启动 supervisord +sudo systemctl is-enable supervisord +# 检查 supervisord 状态 +sudo systemctl status supervisord +# 更新新的配置到supervisord(不会重启原来已运行的程序) +sudo supervisorctl update +# 载入所有配置文件,并按新的配置启动、管理所有进程(会重启原来已运行的程序) +sudo supervisorctl reload +# 启动某个进程 +sudo supervisorctl start xxx +# 重启某个进程 +sudo supervisorctl restart xxx +# 停止某个进程(xxx),xxx为[program:theprogramname]里配置的值 +sudo supervisorctl stop xxx +# 重启所有属于名为groupworker这个分组的进程(start,restart同理) +sudo supervisorctl restart groupworker +# 停止全部进程 +sudo supervisorctl stop all +# 查看服务状态 +sudo supervisorctl status +``` + +### 程序配置 + +```conf +[program:ckadminnetcore] +command=dotnet CK.Admin.WebApi.dll --urls http://[*]:8888 +directory=/root/www/ckadminnetcore/publish +environment=ASPNETCORE_ENVIRONMENT=Production +user=root +autostart=true +autorestart=true +stderr_logfile=/var/log/ckadminnetcore/err.log +stdout_logfile=/var/log/ckadminnetcore/out.log +stopasgroup=true +``` + +## 安装 FastGithub + +```shell +wget https://gitee.com/chcrazy/FastGithub/releases/download/2.1.4/fastgithub_linux-x64.zip +dnf install -y libicu +# 配置系统代理 +# 配置系统环境变量 +vim /etc/profile + +# 尾行加上 +export http_proxy="http://127.0.0.1:38457" +export https_proxy="http://127.0.0.1:38457" + +# 生效 +source /etc/profile + +unzip fastgithub_linux-x64.zip +cd fastgithub_linux-x64 +./fastgithub +``` + +## 安装 ohmyzsh + +### 安装zsh + +```shell +dnf install -y zsh +``` + +### 脚本安装 + +| **Method** | **Command** | +| ------------------------------------------------ | ------------------------------------------------------------ | +| **curl** | `sh -c "$(curl -fsSL https://install.ohmyz.sh/)"` | +| **wget** | `sh -c "$(wget -O- https://install.ohmyz.sh/)"` | +| **fetch** | `sh -c "$(fetch -o - https://install.ohmyz.sh/)"` | +| 国内curl[镜像](https://gitee.com/pocmon/ohmyzsh) | `sh -c "$(curl -fsSL https://gitee.com/pocmon/ohmyzsh/raw/master/tools/install.sh)"` | +| 国内wget[镜像](https://gitee.com/pocmon/ohmyzsh) | `sh -c "$(wget -O- https://gitee.com/pocmon/ohmyzsh/raw/master/tools/install.sh)"` | + +注意:同意使用 Oh-my-zsh 的配置模板覆盖已有的 `.zshrc`。 + +![image-20250417105106956](http://minio.wenyongdalucky.club:9000/hexo/image-20250417105106956.png) + +### 从`.bashrc`中迁移配置(可选) + +如果之前在使用`bash`时自定义了一些环境变量、别名等,那么在切换到`zsh`后,你需要手动迁移这些自定义配置。 + +```shell +# 查看bash配置文件,并手动复制自定义配置 +cat ~/.bashrc +# 编辑zsh配置文件,并粘贴自定义配置 +vim ~/.zshrc +# 启动新的zsh配置 +source ~/.zshrc +``` + +`root`用户在执行`sudo su`命令后,再运行上述代码查看、手动复制、粘贴自定义配置。 + +### 配置 + +```shell +vim ~/.zshrc + +# 修改主题 +# ZSH_THEME='robbyrussell' +ZSH_THEME='agnoster' + +source ~/.zshrc +``` + +### 切换为默认shell + +```shell +dnf install util-linux-user -y +chsh -s /bin/zsh + +#查看默认Shell +echo $SHELL +``` + +### 主题 + +#### Powerlevel10K + +根据 [What’s the best theme for Oh My Zsh?](https://www.slant.co/topics/7553/~theme-for-oh-my-zsh) 中的排名,以及自定义化、美观程度,强烈建议使用 [powerlevel10k](https://github.com/romkatv/powerlevel10k) 主题。 + +```shell +git clone --depth=1 https://github.com/romkatv/powerlevel10k.git ${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/themes/powerlevel10k + +# 中国用户可以使用 gitee.com 上的官方镜像加速下载 +git clone --depth=1 https://gitee.com/romkatv/powerlevel10k.git ${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/themes/powerlevel10k +``` + +在 `~/.zshrc` 设置 `ZSH_THEME="powerlevel10k/powerlevel10k"`。接下来,终端会自动引导你配置 `powerlevel10k`,若已配置可输入`p10k configure`重新进行配置引导。 + +若是安装的AlmaLinux minimal系统,需先通过`dnf`安装 `"Development Tools"`,再执行`source ~/.zshrc` + +```bash +dnf groupinstall "Development Tools" -y +``` + +#### robbyrussell + +显示路径每一级的首字母和最后一级目录的全名 + +默认主题只显示路径最后一级名字,其他的一些主题可能显示完整路径,但那太长了。我只发现主题 fishy 是这样显示的,这也和 fish 命令行相同。 + +配置 + +```shell +vim ~/.oh-my-zsh/themes/robbyrussell.zsh-theme +``` + +添加一个函数 + +```shell +_fishy_collapsed_wd() { + local i pwd + pwd=("${(s:/:)PWD/#$HOME/~}") + if (( $#pwd > 1 )); then + for i in {1..$(($#pwd-1))}; do + if [[ "$pwd[$i]" = .* ]]; then + pwd[$i]="${${pwd[$i]}[1,2]}" + else + pwd[$i]="${${pwd[$i]}[1]}" + fi + done + fi + echo "${(j:/:)pwd}" +} +``` + +在 PROMPT 那一行,把表示目录的 `%c` (其他主题可能是 `%C`,`%~` ,`%2` 等)改成 `$(_fishy_collapsed_wd)`,重启 zsh 即可。 + +显示用户 + +在 PROMPT 前适当加上 `%{$fg_bold[blue]%}${USER}` 即可。左边的是在设置颜色。 + +显示上一条命令返回值 + +这个默认主题,当返回值为 0 时箭头为绿色,非 0 时为红色,我想让他非 0 时显示返回值。 + +解决方案:PROMPT 适当位置加上 `%?`,记得不要写成 `$?` 因为后者只显示第一个数字(好像是这样,我没仔细查过)。 + +最终配置 + +```shell +if [ `id -u` -eq 0 ];then + PROMPT="%(?:%{$fg_bold[red]%}root %{$fg_bold[green]%}➜ :%{$fg_bold[red]%}root %{$fg_bold[red]%}%? ➜ )" +else + PROMPT="%(?:%{$fg_bold[blue]%}${USER} %{$fg_bold[green]%}➜ :%{$fg_bold[blue]%}${USER} %{$fg_bold[red]%}%? ➜ )" +fi + +_fishy_collapsed_wd() { + local i pwd + pwd=("${(s:/:)PWD/#$HOME/~}") + if (( $#pwd > 1 )); then + for i in {1..$(($#pwd-1))}; do + if [[ "$pwd[$i]" = .* ]]; then + pwd[$i]="${${pwd[$i]}[1,2]}" + else + pwd[$i]="${${pwd[$i]}[1]}" + fi + done + fi + echo "${(j:/:)pwd}" +} +PROMPT+=' %{$fg[yellow]%}$(_fishy_collapsed_wd)%{$reset_color%} $(git_prompt_info)' + + +ZSH_THEME_GIT_PROMPT_PREFIX="%{$fg[blue]%}git:(%{$fg[yellow]%}" +ZSH_THEME_GIT_PROMPT_SUFFIX="%{$reset_color%} " +ZSH_THEME_GIT_PROMPT_DIRTY="%{$fg[blue]%}) %{$fg[yellow]%}✗" +ZSH_THEME_GIT_PROMPT_CLEAN="%{$fg[blue]%})" +``` + +### 插件 + +> `oh-my-zsh` 已经内置了 `git` 插件,内置插件可以在 `~/.oh-my-zsh/plugins` 中查看,下面介绍一下我常用的插件,更多插件可以在 [awesome-zsh-plugins](https://github.com/unixorn/awesome-zsh-plugins) 里查看。 + +#### zsh -autosuggestions + +[zsh-autosuggestions](https://github.com/zsh-users/zsh-autosuggestions) 是一个命令提示插件,当你输入命令时,会自动推测你可能需要输入的命令,按下右键可以快速采用建议。 + +![zsh-autosuggestions自动补全](https://cdn.haoyep.com/gh/leegical/Blog_img/cdnimg/202401012250028.png) + +安装方式:把插件下载到本地的 `~/.oh-my-zsh/custom/plugins` 目录。 + +```shell +git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions + +# 中国用户可以使用下面任意一个加速下载 +# 加速1 +git clone https://github.moeyy.xyz/https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions +# 加速2 +git clone https://gh.xmly.dev/https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions +# 加速3 +git clone https://gh.api.99988866.xyz/https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions +``` + +#### zsh-syntax-highlighting + +[zsh-syntax-highlighting](https://github.com/zsh-users/zsh-syntax-highlighting) 是一个命令语法校验插件,在输入命令的过程中,若指令不合法,则指令显示为红色,若指令合法就会显示为绿色。 + +![命令语法校验](https://cdn.haoyep.com/gh/leegical/Blog_img/cdnimg/202401012252786.png) + +安装方式:把插件下载到本地的 `~/.oh-my-zsh/custom/plugins` 目录。 + +```shell +git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting + +# 中国用户可以使用下面任意一个加速下载 +# 加速1 +git clone https://github.moeyy.xyz/https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting +# 加速2 +git clone https://gh.xmly.dev/https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting +# 加速3 +git clone https://gh.api.99988866.xyz/https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting +``` + +#### z + +`oh-my-zsh` 内置了 `z` 插件。`z` 是一个文件夹快捷跳转插件,对于曾经跳转过的目录,只需要输入最终目标文件夹名称,就可以快速跳转,避免再输入长串路径,提高切换文件夹的效率。 + +![使用z跳转目录](https://cdn.haoyep.com/gh/leegical/Blog_img/cdnimg/202401012254065.png) + +#### extract + +`oh-my-zsh` 内置了 `extract` 插件。`extract` 用于解压任何压缩文件,不必根据压缩文件的后缀名来记忆压缩软件。使用 `x` 命令即可解压文件。 + +![extract 解压](https://cdn.haoyep.com/gh/leegical/Blog_img/cdnimg/202401012259966.png) + +#### web-search + +oh-my-zsh 内置了 `web-search` 插件。`web-search` 能让我们在命令行中使用搜索引擎进行搜索。使用`搜索引擎关键字+搜索内容` 即可自动打开浏览器进行搜索。效果如下: + +![web-search搜索](https://cdn.haoyep.com/gh/leegical/Blog_img/cdnimg/202401012302476.png) + +### 启用插件 + +修改`~/.zshrc`中插件列表为: + +```shell +plugins=(git zsh-autosuggestions zsh-syntax-highlighting z extract web-search) +``` + +开启新的 Shell 或执行 `source ~/.zshrc`,就可以开始体验插件。 + +### 卸载 + +```shell +uninstall_oh_my_zsh +Are you sure you want to remove Oh My Zsh? [y/N] Y +``` + +### 手动更新 + +```shell +upgrade_oh_my_zsh +``` + +### 安装Nerd Fonts + +```shell +unzip nerd-fonts.zip + +``` + +运行install.sh提示/usr/bin/env: ‘bash\r’: No such file or directory + +```shell +vim install.sh + +:set ff +# 可以看到fileformat=dos + +:set ff=unix +:set ff +# 可以看到fileformat=unix 即保存成功 +:wq +``` + + + +## 安装 ElasticSearch + +```shell +cd /etc/yum.repos.d +vim elasticsearch.repo + +[elasticsearch] +name=Elasticsearch repository for 8.x packages +baseurl=https://artifacts.elastic.co/packages/8.x/yum +gpgcheck=1 +gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch +enabled=0 +autorefresh=1 +type=rpm-md + +dnf install --enablerepo=elasticsearch elasticsearch -y +``` + +## 安装 Rsync + +```shell +dnf install rsync -y +``` + +### 配置 + +```shell +vim /etc/rsyncd.conf + +# /etc/rsyncd: configuration file for rsync daemon mode + +# See rsyncd.conf man page for more options. + +# configuration example: + +uid = root +gid = root +ignore errors +hosts allow = 10.0.3.0/24 +secrets file = /etc/rsyncd.secrets +# use chroot = yes +# max connections = 4 +# pid file = /var/run/rsyncd.pid +# exclude = lost+found/ +# transfer logging = yes +# timeout = 900 +# ignore nonreadable = yes +# dont compress = *.gz *.tgz *.zip *.z *.Z *.rpm *.deb *.bz2 + +# [ftp] +# path = /home/ftp +# comment = ftp export area +# 同步模块名称 +[syncwspswwwroot] +# 服务器同步的路径 +path = /root/wwwroot +log file = /var/log/rsync.log +# 只读模式 不允许客户端向同步路径进行同步上传文件 +read only = yes + +[mysql_bakup] +path = /root/mysql_bakup +read only = yes +``` + +### 命令 + + + +## 安装 Jenkins + +安装 Java + +Jenkins 需要 Java JRE v11 或 v17。 因此,使用以下命令安装 Java: + +```shell +sudo dnf install java-17-openjdk +``` + +验证 Java 版本: + +```shell +java -version +``` + +现在我们将 Jenkins 存储库添加到您的 AlmaLinux/Rocky Linux。 首先,添加创建存储库: + +```shell +wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo +``` + +接下来,将 Jenkins 密钥导入系统: + +```shell +rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io-2023.key +``` + +然后,使用以下命令安装 Jenkins: + +```shell +sudo dnf makecache +sudo dnf install fontconfig +sudo dnf install jenkins +``` + +安装完成后,通过执行以下命令启动并验证 Jenkins 的状态: + +```shell +sudo systemctl start jenkins +sudo systemctl status jenkins +``` + + + +```shell +[MIRROR] jenkins-2.440.1-1.1.noarch.rpm: Curl error (60): SSL peer certificate or SSH remote key was not OK for https://mirrors.tuna.tsinghua.edu.cn/jenkins/redhat-stable/jenkins-2.440.1-1.1.noarch.rpm [SSL certificate problem: certificate is not yet valid] +``` + +### 修改端口 + +修改为想要的端口 + +```shell +# 进入目录 +cd /usr/lib/systemd/system +vim jenkins.service +# 或者 +vim /usr/lib/systemd/system/jenkins.service + +# Port to listen on for HTTP requests. Set to -1 to disable. +# To be able to listen on privileged ports (port numbers less than 1024), +# add the CAP_NET_BIND_SERVICE capability to the AmbientCapabilities +# directive below. +Environment="JENKINS_PORT=16060" + +# 重新加载配置文件 +systemctl daemon-reload + +``` + +### 修改用户及用户组 + +修改为root + +```shell +# 进入目录 +cd /usr/lib/systemd/system +vim jenkins.service +# 或者 +vim /usr/lib/systemd/system/jenkins.service + + +# Unix account that runs the Jenkins daemon +# Be careful when you change this, as you need to update the permissions of +# $JENKINS_HOME, $JENKINS_LOG, and (if you have already run Jenkins) +# $JENKINS_WEBROOT. +User=root +Group=root + +# 重新加载配置文件 +systemctl daemon-reload +``` + +### 解决Jenkins部分汉化、汉化不全有效办法 + +添加 -Duser.language=C.UTF-8 + +```shell +# 进入目录 +cd /usr/lib/systemd/system +vim jenkins.service +# 或者 +vim /usr/lib/systemd/system/jenkins.service + +# Arguments for the Jenkins JVM +Environment="JAVA_OPTS=-Djava.awt.headless=true -Duser.language=C.UTF-8" + +# 重新加载配置文件 +systemctl daemon-reload +``` + +### 解决 Jenkins 无法拉取TLS 1.0的老旧SVN项目 + +主要是Java JDK禁用了TLS 1.0协议,需修改`java.security`配置文件进行开启 + +首先找到启动Jenkins的Java JDK的目录,找到`conf/` + +使用`vim`进行编辑查看,可以看到如下有 TLSv1, TLSv1.1,3DES_EDE_CBC,编辑将其删除 + +```yaml +# Example: +# jdk.tls.disabledAlgorithms=MD5, SSLv3, DSA, RSA keySize < 2048, \ +# rsa_pkcs1_sha1, secp224r1 +jdk.tls.disabledAlgorithms=SSLv3, TLSv1, TLSv1.1, DTLSv1.0, RC4, DES, \ + MD5withRSA, DH keySize < 1024, EC keySize < 224, 3DES_EDE_CBC, anon, NULL, \ + ECDH + +``` + +`:wq`保存即可。 + +### 升级 + +#### AlmaLinux + +升级前请先备份上述 `jenkins.service` 配置文件 + +```bash +cp -r /usr/lib/systemd/system/jenkins.service /usr/lib/systemd/system/jenkins.service.bak +``` + +使用dnf包管理器进行升级 + +```bash +dnf upgrade jenkins +``` + +## 安装 Certbot + +**Certbot** 是由 **Electronic Frontier Foundation (EFF)** 提供的一个开源命令行工具,用于自动化从 Let’s Encrypt 获取和管理 SSL 证书。Certbot 会自动为你处理证书申请、安装和续期等过程。,用于自动化整个 SSL 证书的管理流程。它可以做以下几件事: + +- **申请证书**:使用 ACME 协议从 Let’s Encrypt 获取证书。 +- **验证域名所有权**:通过 HTTP-01 或 DNS-01 验证确保你拥有该域名。 +- **安装证书**:将证书自动安装到你的 Web 服务器,并配置相关的加密参数。 +- **续期证书**:定期自动续期证书,避免证书过期。 + +Certbot 的核心工作是通过 **ACME 协议**(自动证书管理环境)与 Let’s Encrypt 通信。ACME 是一套标准协议,用于自动化证书申请、验证和安装的过程。Certbot 使用 ACME 协议与 Let’s Encrypt 进行通信,确保你的网站能够通过安全的 HTTPS 连接。 + +推荐使用 Linux 的 snap 包管理工具安装Certbot + +首先需要安装 snap 包管理工具 + +> [Installing snap on Rocky Linux | Snapcraft documentation参考官网](https://snapcraft.io/docs/installing-snap-on-rocky) + +### AlmaLinux 安装 snapd + +AlmaLinux OS的快照包可以在Enterprise Linux Extra Packages(EPEL)存储库中找到。使用以下命令将EPEL存储库添加到AlmaLinux OS系统: + +```shell +dnf install epel-release +dnf upgrade +``` + +将EPEL存储库添加到AlmaLinux OS安装中后,只需安装Snapd包(以root/或sudo身份): + +```shell +dnf install snapd +``` + +安装后,需要启用管理主快照通信插座的systemd单元: + +```shell +systemctl enable --now snapd.socket +``` + +要启用经典snap支持,请输入以下内容以在`/var/lib/snapd/snap`和`/snap`之间创建符号链接: + +```shell +ln -s /var/lib/snapd/snap /snap +``` + +退出并再次重新登录或重新启动系统以确保快照的路径正确更新。 +Snap现已安装完毕,即可运行! + +支持snap后可以使用如下命令安装Certbot + +```shell +snap install --classic certbot +``` + +创建一个符号链接,确保可以执行certbot命令(相当于快捷方式) + +```shell +ln -s /snap/bin/certbot /usr/bin/certbot +``` + +## 安装 frp + +### 配置服务端 (frps) 为服务 + +在公网服务器上部署服务端 + +创建 Systemd 服务文件 + +```bash +sudo vim /etc/systemd/system/frps.service +``` + +写入配置内容 + +```toml +[Unit] +# 服务描述 +Description=FRP Server Daemon +# 在网络服务启动后才启动 +After=network.target syslog.target +Wants=network.target + +[Service] +Type=simple +# 启动命令:请修改为你的 frps 和配置文件实际路径 +ExecStart=/usr/local/frp/frps -c /usr/local/frp/frps.toml +# 退出后自动重启 +Restart=on-failure +RestartSec=5s + +[Install] +WantedBy=multi-user.target +``` + +启动并设置开机自启 + +```bash +# 重载 systemd 配置 +sudo systemctl daemon-reload + +# 启动 frps 服务 +sudo systemctl start frps + +# 设置开机自启 +sudo systemctl enable frps + +# 查看运行状态 +sudo systemctl status frps +``` + +### 配置客户端 (frpc) 为服务 + +在内网机器上部署客户端 + +创建 Systemd 服务文件 + +```bash +sudo vim /etc/systemd/system/frpc.service +``` + +写入配置内容 + +```toml +[Unit] +Description=FRP Client Daemon +After=network.target syslog.target +Wants=network.target + +[Service] +Type=simple +# 启动命令:请修改为你的 frpc 和配置文件实际路径 +ExecStart=/usr/local/frp/frpc -c /usr/local/frp/frpc.toml +# 这里的 restart 非常重要,防止因为断网导致客户端停止运行 +Restart=on-failure +RestartSec=5s + +[Install] +WantedBy=multi-user.target +``` + +启动并设置开机自启 + +```bash +# 重载 systemd 配置 +sudo systemctl daemon-reload + +# 启动 frpc 服务 +sudo systemctl start frpc + +# 设置开机自启 +sudo systemctl enable frpc + +# 查看运行状态 +sudo systemctl status frpc +``` + diff --git a/source/_posts/Linux/image-20240130093530200.png b/source/_posts/Linux/image-20240130093530200.png new file mode 100644 index 0000000..7100160 Binary files /dev/null and b/source/_posts/Linux/image-20240130093530200.png differ diff --git a/source/_posts/Linux/image-20240130093629718.png b/source/_posts/Linux/image-20240130093629718.png new file mode 100644 index 0000000..47e6902 Binary files /dev/null and b/source/_posts/Linux/image-20240130093629718.png differ diff --git a/source/_posts/Linux/image-20240130093817210.png b/source/_posts/Linux/image-20240130093817210.png new file mode 100644 index 0000000..ccb6f98 Binary files /dev/null and b/source/_posts/Linux/image-20240130093817210.png differ diff --git a/source/_posts/Linux/image-20240130093857754.png b/source/_posts/Linux/image-20240130093857754.png new file mode 100644 index 0000000..5251314 Binary files /dev/null and b/source/_posts/Linux/image-20240130093857754.png differ diff --git a/source/_posts/Linux/image-20240130093939297.png b/source/_posts/Linux/image-20240130093939297.png new file mode 100644 index 0000000..3da0adc Binary files /dev/null and b/source/_posts/Linux/image-20240130093939297.png differ diff --git a/source/_posts/Linux/image-20240130094105250.png b/source/_posts/Linux/image-20240130094105250.png new file mode 100644 index 0000000..857438d Binary files /dev/null and b/source/_posts/Linux/image-20240130094105250.png differ diff --git a/source/_posts/Linux/image-20240130094218593.png b/source/_posts/Linux/image-20240130094218593.png new file mode 100644 index 0000000..3d837a1 Binary files /dev/null and b/source/_posts/Linux/image-20240130094218593.png differ diff --git a/source/_posts/Linux/image-20240130094244545.png b/source/_posts/Linux/image-20240130094244545.png new file mode 100644 index 0000000..e92db5b Binary files /dev/null and b/source/_posts/Linux/image-20240130094244545.png differ diff --git a/source/_posts/Linux/image-20240130094253931.png b/source/_posts/Linux/image-20240130094253931.png new file mode 100644 index 0000000..56046c9 Binary files /dev/null and b/source/_posts/Linux/image-20240130094253931.png differ diff --git a/source/_posts/Linux/image-20240130094329028.png b/source/_posts/Linux/image-20240130094329028.png new file mode 100644 index 0000000..0beea06 Binary files /dev/null and b/source/_posts/Linux/image-20240130094329028.png differ diff --git a/source/_posts/Linux/image-20240130094418379.png b/source/_posts/Linux/image-20240130094418379.png new file mode 100644 index 0000000..25f291c Binary files /dev/null and b/source/_posts/Linux/image-20240130094418379.png differ diff --git a/source/_posts/Linux/image-20240130094442884.png b/source/_posts/Linux/image-20240130094442884.png new file mode 100644 index 0000000..c301484 Binary files /dev/null and b/source/_posts/Linux/image-20240130094442884.png differ diff --git a/source/_posts/Linux/image-20240130094540550.png b/source/_posts/Linux/image-20240130094540550.png new file mode 100644 index 0000000..e96a5ad Binary files /dev/null and b/source/_posts/Linux/image-20240130094540550.png differ diff --git a/source/_posts/Linux/image-20240201162619794.png b/source/_posts/Linux/image-20240201162619794.png new file mode 100644 index 0000000..4fb50b1 Binary files /dev/null and b/source/_posts/Linux/image-20240201162619794.png differ diff --git a/source/_posts/Linux/image-20240201162720183.png b/source/_posts/Linux/image-20240201162720183.png new file mode 100644 index 0000000..100b76e Binary files /dev/null and b/source/_posts/Linux/image-20240201162720183.png differ diff --git a/source/_posts/Linux/image-20240201162906958.png b/source/_posts/Linux/image-20240201162906958.png new file mode 100644 index 0000000..c144948 Binary files /dev/null and b/source/_posts/Linux/image-20240201162906958.png differ diff --git a/source/_posts/Linux/image-20240201163026019.png b/source/_posts/Linux/image-20240201163026019.png new file mode 100644 index 0000000..75a6f59 Binary files /dev/null and b/source/_posts/Linux/image-20240201163026019.png differ diff --git a/source/_posts/Linux/image-20240201163052988.png b/source/_posts/Linux/image-20240201163052988.png new file mode 100644 index 0000000..b7f9c95 Binary files /dev/null and b/source/_posts/Linux/image-20240201163052988.png differ diff --git a/source/_posts/Linux/image-20240201163211759.png b/source/_posts/Linux/image-20240201163211759.png new file mode 100644 index 0000000..65c1522 Binary files /dev/null and b/source/_posts/Linux/image-20240201163211759.png differ diff --git a/source/_posts/Linux/image-20240201163229297.png b/source/_posts/Linux/image-20240201163229297.png new file mode 100644 index 0000000..b794dd8 Binary files /dev/null and b/source/_posts/Linux/image-20240201163229297.png differ diff --git a/source/_posts/Linux/image-20240201163300368.png b/source/_posts/Linux/image-20240201163300368.png new file mode 100644 index 0000000..5c5af6d Binary files /dev/null and b/source/_posts/Linux/image-20240201163300368.png differ diff --git a/source/_posts/Linux/image-20240201163342757.png b/source/_posts/Linux/image-20240201163342757.png new file mode 100644 index 0000000..f03d22a Binary files /dev/null and b/source/_posts/Linux/image-20240201163342757.png differ diff --git a/source/_posts/Linux/image-20240201163506115.png b/source/_posts/Linux/image-20240201163506115.png new file mode 100644 index 0000000..89323c7 Binary files /dev/null and b/source/_posts/Linux/image-20240201163506115.png differ diff --git a/source/_posts/Linux/image-20240201163544310.png b/source/_posts/Linux/image-20240201163544310.png new file mode 100644 index 0000000..58167b7 Binary files /dev/null and b/source/_posts/Linux/image-20240201163544310.png differ diff --git a/source/_posts/Linux/image-20240201163800217.png b/source/_posts/Linux/image-20240201163800217.png new file mode 100644 index 0000000..734dc5c Binary files /dev/null and b/source/_posts/Linux/image-20240201163800217.png differ diff --git a/source/_posts/Linux/image-20240201163826924.png b/source/_posts/Linux/image-20240201163826924.png new file mode 100644 index 0000000..4d1dc83 Binary files /dev/null and b/source/_posts/Linux/image-20240201163826924.png differ diff --git a/source/_posts/Linux/image-20240201163847295.png b/source/_posts/Linux/image-20240201163847295.png new file mode 100644 index 0000000..31163a2 Binary files /dev/null and b/source/_posts/Linux/image-20240201163847295.png differ diff --git a/source/_posts/Linux/image-20240227123457743.png b/source/_posts/Linux/image-20240227123457743.png new file mode 100644 index 0000000..55c6662 Binary files /dev/null and b/source/_posts/Linux/image-20240227123457743.png differ diff --git a/source/_posts/Linux/image-20240227123607562.png b/source/_posts/Linux/image-20240227123607562.png new file mode 100644 index 0000000..ab25cc6 Binary files /dev/null and b/source/_posts/Linux/image-20240227123607562.png differ diff --git a/source/_posts/Linux/image-20240227123637191.png b/source/_posts/Linux/image-20240227123637191.png new file mode 100644 index 0000000..eea1854 Binary files /dev/null and b/source/_posts/Linux/image-20240227123637191.png differ diff --git a/source/_posts/Maven.md b/source/_posts/Maven.md new file mode 100644 index 0000000..37e1a28 --- /dev/null +++ b/source/_posts/Maven.md @@ -0,0 +1,14 @@ +--- +title: Maven +date: 2025-11-06 10:15:58 +tags: +--- + +# 命令 + +## mvn package + + + +## mvn install + diff --git a/source/_posts/Maven与Gradle.md b/source/_posts/Maven与Gradle.md new file mode 100644 index 0000000..9d5f1af --- /dev/null +++ b/source/_posts/Maven与Gradle.md @@ -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,将其拷贝到项目的根目录下即可。 \ No newline at end of file diff --git a/source/_posts/MySQL.md b/source/_posts/MySQL.md new file mode 100644 index 0000000..7ad58be --- /dev/null +++ b/source/_posts/MySQL.md @@ -0,0 +1,1011 @@ +--- +title: MySQL +date: 2023-09-23 10:30:31 +author: 文永达 +top_img: https://gcore.jsdelivr.net/gh/volantis-x/cdn-wallpaper/abstract/67239FBB-E15D-4F4F-8EE8-0F1C9F3C4E7C.jpeg +--- + + + +# mysqldump备份数据库 + +## 备份实例下的所有库 + +```shell +mysqldump -uroot -p -A > all.sql +``` + +## 备份单个指定数据库 + +```shell +mysqldump -uroot -p test > test.sql +``` + +## 备份多个指定数据库 + +```shell +mysqldump -uroot -p test1 test2 > test12.sql +``` + +## 备份指定数据库中的单个表 + +```shell +mysqldump -uroot -p test user > test.user.sql +``` + +## 备份指定数据库中的多个表 + +```shell +mysqldump -uroot -p test user role > test.ur.sql +``` + +## 备份数据库表结构只包含DDL语句 + +```shell +# --no-data 或 -d +mysqldump -uroot -p test --no-data > test.sql +``` + +## 备份数据库带库名 + +```shell +mysqldump -uroot -p -B test > test.sql +``` + +# Xtrabackup备份数据库 + +## 安装 + +### wget方式 + +1. 安装qpress rpm包。 + ```shell + wget https://repo.percona.com/yum/release/7/RPMS/x86_64/qpress-11-1.el9.x86_64.rpm + rpm -ivh qpress-11-1.el9.x86_64.rpm + ``` + +2. 安装Percona XtraBackup + + - MySQL 5.6、5.7,以下载并安装Percona XtraBackup 2.4.9为例 + ```shell + wget https://downloads.percona.com/downloads/Percona-XtraBackup-2.4/Percona-XtraBackup-2.4.29/binary/redhat/9/x86_64/percona-xtrabackup-24-2.4.29-1.el9.x86_64.rpm + + rpm -ivh percona-xtrabackup-24-2.4.29-1.el9.x86_64.rpm + + warning: percona-xtrabackup-24-2.4.29-1.el9.x86_64.rpm: Header V4 RSA/SHA256 Signature, key ID 8507efa5: NOKEY + error: Failed dependencies: + libatomic.so.1()(64bit) is needed by percona-xtrabackup-24-2.4.29-1.el9.x86_64 + libev.so.4()(64bit) is needed by percona-xtrabackup-24-2.4.29-1.el9.x86_64 + perl(DBD::mysql) is needed by percona-xtrabackup-24-2.4.29-1.el9.x86_64 + rsync is needed by percona-xtrabackup-24-2.4.29-1.el9.x86_64 + + dnf install libatomic -y + dnf install libev -y + dnf install -y rsync + dnf install perl-DBD-MySQL + + ``` + + - MySQL 8.0,以下载并安装Percona XtraBackup 8.0为例 + ```shell + wget https://downloads.percona.com/downloads/Percona-XtraBackup-8.0/Percona-XtraBackup-8.0.32-26/binary/redhat/7/x86_64/percona-xtrabackup-80-8.0.32-26.1.el7.x86_64.rpm + + rpm -ivh percona-xtrabackup-80-8.0.32-26.1.el7.x86_64.rpm --nodeps --force + ``` + +## 备份 + +#### 全量备份 + +```shell +innobackupex --user=root --password=123456 --host=127.0.0.1 ~/mysql_bakup/ + +#语法解释说明: +#--user=root 指定备份用户 +#--password=123456 指定备份用户密码 +#--host  指定主机 +#~/mysql_bakup/  指定备份目录 +``` + +执行命令后。可看到备份时间的文件夹 + +![image-20240130140632864](MySQL/image-20240130140632864.png) + +文件夹内容如下 + +![image-20240130140723465](MySQL/image-20240130140723465.png) + +#### 差异备份 + +首先需要全量备份一次 + +```shell +innobackupex --user=root --password=123456 --host=127.0.0.1 ~/mysql_bakup/ +``` + +然后按照全量备份生成的备份目录为基础 + +```shell +innobackupex --user=root --password=123456 --host=127.0.0.1 --incremental ~/mysql_bakup/ --incremental-basedir=~/mysql_bakup/2024-01-30_14-03-42/ +``` + +![image-20240130151257100](MySQL/image-20240130151257100.png) + +差异备份需要使用参数--incremental指定需要备份到哪个目录,使用incremental-dir指定全备目录 + +查看备份数据 + +![image-20240130172206916](MySQL/image-20240130172206916.png) + +## 恢复 + +#### 全量备份恢复 + +```shell +# 停止目标服务器上的mysql +systemctl stop mysqld +``` + +![image-20240130142057208](MySQL/image-20240130142057208.png) + +同步服务器上的备份文件至本地 + +```shell +# 同步服务器上的备份文件夹至本地 +rsync -av root@10.0.3.3::mysql_bakup ~/mysql_bakup +``` + +![image-20240130142150299](MySQL/image-20240130142150299.png) + +创建本地数据目录备份 + +```shell +cp -r /var/lib/mysql /var/lib/mysqlbakup +``` + +合并数据日志,使数据文件处于一致性的状态 + +```shell +innobackupex --apply-log ~/mysql_bakup/2024-01-30_14-03-42/ +``` + +![image-20240130142410127](MySQL/image-20240130142410127.png) + +删除数据目录 + +```shell +rm -rf /var/lib/mysql +``` + +进行数据恢复 + +```shell +innobackupex --copy-back 2024-01-30_14-03-42/ +``` + +![image-20240130142551097](MySQL/image-20240130142551097.png) + +赋予权限 + +```shell +chown mysql:mysql /var/lib/mysql -R +``` + +临时关闭SELinux + +```she +setenforce 0 +``` + +永久关闭SELinux + +#### 差异备份恢复 + +```shell +# 停止目标服务器上的mysql +systemctl stop mysqld +``` + +![image-20240130142057208](MySQL/image-20240130142057208.png) + +删除数据目录 + +```shell +rm -rf /var/lib/mysql +``` + +合并全备数据目录,确保数据的一致性 + +```shell +innobackupex --apply-log --redo-only ~/mysql_bakup/2024-01-30_14-03-42/ +``` + +将差异备份数据合并到全备数据目录当中 + +```shell +innobackupex --apply-log --redo-only ~/mysql_bakup/2024-01-30_14-03-42/ --incremental-dir=~/mysql_bakup/2024-01-30_15-12-28/ +``` + +恢复数据 + +```shell +innobackupex --copy-back ~/mysql_bakup/2024-01-30_14-03-42/ +``` + +赋予权限 + +```shell +chown mysql:mysql /var/lib/mysql -R +``` + +临时关闭SELinux + +```she +setenforce 0 +``` + +永久关闭SELinux + +# Windows 下安装 绿色版 + +先下载[MySQL :: Download MySQL Community Server](https://dev.mysql.com/downloads/mysql/) + +![image-20231007124717167](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20231007124717167.png) + +1. 解压下载好的压缩包 + ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210120105901505.png#pic_left) + +2. 解压后得到 + ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210120105247435.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0NXZVFpbg==,size_16,color_FFFFFF,t_70#pic_center) + +3. 新建一个 `my.ini`文件 + ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210120110525427.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0NXZVFpbg==,size_16,color_FFFFFF,t_70#pic_center) + +4. 解压后的mysql根目录下没有my.ini文件,自己去网上找一份就可或者使用我在后面给出的代码。.ini文件会在初始化mysql中用到 + ```ini + # For advice on how to change settings please see + # http=//dev.mysql.com/doc/refman/5.7/en/server-configuration-defaults.html + # *** DO NOT EDIT THIS FILE. It's a template which will be copied to the + # *** default location during install, and will be replaced if you + # *** upgrade to a newer version of MySQL. + + [client] + port = 3306 + default-character-set = utf8 + + [mysqld] + + # Remove leading # and set to the amount of RAM for the most important data + # cache in MySQL. Start at 70% of total RAM for dedicated server, else 10%. + # innodb_buffer_pool_size = 128M + + # Remove leading # to turn on a very important data integrity option= logging + # changes to the binary log between backups. + # log_bin + port = 3306 + # These are commonly set, remove the # and set as required. + basedir="D:\app\mysql-5.7.43-winx64" + datadir="D:\app\mysql-5.7.43-winx64\data" + # server_id = ..... + character_set_server = utf8 + + # Remove leading # to set options mainly useful for reporting servers. + # The server defaults are faster for transactions and fast SELECTs. + # Adjust sizes as needed, experiment to find the optimal values. + # join_buffer_size = 128M + # sort_buffer_size = 2M + # read_rnd_buffer_size = 2M + + sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES + + ``` + +5. 修改ini配置文件中的安装目录和数据存放目录修改为mysql文件的路径 + +6. \#设置mysql的安装目录 + basedir=D:\app\mysql-5.7.43-winx64 + \#设置mysql数据库的数据的存放目录 + datadir=D:\app\mysql-5.7.43-winx64\data + +7. 打开cmd,初始化数据库 + ```powershell + mysqld --initialize + ``` + +8. 初始化完成后,mysqld根目录下会自动新增data文件夹 + ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210120153744844.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0NXZVFpbg==,size_16,color_FFFFFF,t_70#pic_center) + +9. 打开data文件夹,找到.err后缀文本打开 + ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210120153933128.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0NXZVFpbg==,size_16,color_FFFFFF,t_70#pic_center) + +10. 找到文件password位置,红色框中为数据库初始化密码,后续修改初始化密码使用 + ```err + 2023-10-07T04:37:02.330654Z 1 [Note] A temporary password is generated for root@localhost: (iw?Mw:Vs7n& + ``` + +11. 安装数据库 + ```powershell + mysqld --install <服务名> + ``` + +12. 启动服务 + ```powershell + net start mysql + ``` + +13. 关闭服务 + ```powershell + net stop mysql + ``` + +14. 修改初始密码 + + - 登录 + ```powershell + mysql -uroot -p'你的初始密码,步骤4中红框里的字符' + ``` + + - 修改密码为 123456 + ```mysql + ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'root'; + ``` + +15. 服务卸载 + ```powershell + net stop mysql + mysqld --remove + ``` + +# Linux 安装MySQL + +## 安装 + +1. 在浏览器下载Linux系统的MySQL客户端安装包。建议您下载的MySQL客户端版本高于已创建的GaussDB(for MySQL)实例中数据库版本。 + 在下载页面找到对应版本[链接](https://dev.mysql.com/downloads/file/?id=496982),以mysql-community-client-8.0.21-1.el6.x86_64为例,打开页面后,即可下载安装包。 + ![img](MySQL/zh-cn_image_0000001729034977.png) + +2. 将安装包上传到ECS。 + +3. 执行以下命令安装MySQL客户端。 + rpm -ivh mysql-community-client-8.0.21-1.el6.x86_64.rpm + + > - 如果安装过程中报conflicts,可增加replacefiles参数重新安装,如下: + > + > rpm -ivh --replacefiles mysql-community-client-8.0.21-1.el6.x86_64.rpm + > + > - 如果安装过程中提示需要安装依赖包,可增加nodeps参数重新安装,如下: + > + > rpm -ivh --nodeps mysql-community-client-8.0.21-1.el6.x86_64.rpm + +## 连接 + +1. **mysql -h** <*host*> **-P** *<port>* **-u** <*userName*> **-p** + 示例: + + mysql -h 192.168.0.16 -P 3306 -u root -p + + 参数说明 + + | 参数 | 说明 | + | ------------ | ---------------------------- | + | <*host*> | 获取的读写内网地址。 | + | *<port>* | 获取的数据库端口,默认3306。 | + | <*userName*> | 管理员帐号root。 | + +2. 出现如下提示时,输入数据库帐号对应的密码。 + ```mysql + Enter password: + ``` + +3. 报错mysql: error while loading shared libraries: libssl.so.10: cannot open shared object file: No such file or directory + + 下载rpm包: + https://mirrors.aliyun.com/centos/8/AppStream/x86_64/os/Packages/compat-openssl10-1.0.2o-3.el8.x86_64.rpm + 安装rpm包: + + ```shell + rpm -i compat-openssl10-1.0.2o-3.el8.x86_64.rpm + 错误:依赖检测失败: + make 被 compat-openssl10-1:1.0.2o-3.el8.x86_64 需要 + rpm -i compat-openssl10-1.0.2o-3.el8.x86_64.rpm --nodeps --force + + mysql: error while loading shared libraries: libncurses.so.5: cannot open shared object file: No such file or directory + cp /usr/lib64/libncurses.so.6 /usr/lib64/libncurses.so.5 + + mysql: error while loading shared libraries: libtinfo.so.5: cannot open shared object file: No such file or directory + cp /usr/lib64/libtinfo.so.6 /usr/lib64/libtinfo.so.5 + ``` + + + +# Linux 卸载MySQL + +## 二进制方式 + +```shell +rpm -qa | grep -i mysql + +# 如下所示 +mysql-community-release-el7-5.noarch +mysql-community-libs-5.6.51-2.el7.x86_64 +mysql-community-client-5.6.51-2.el7.x86_64 +mysql-community-server-5.6.51-2.el7.x86_64 +mysql-community-common-5.6.51-2.el7.x86_64 + +systemctl stop mysqld + +rpm -ev mysql-community-release-el7-5.noarch +rpm -ev mysql-community-libs-5.6.51-2.el7.x86_64 +rpm -ev mysql-community-client-5.6.51-2.el7.x86_64 +rpm -ev mysql-community-server-5.6.51-2.el7.x86_64 +rpm -ev mysql-community-common-5.6.51-2.el7.x86_64 + +find / -name mysql + +# 如下所示 +/var/lib/mysql +/var/lib/mysql/mysql +/usr/local/mysql +/usr/lib64/mysql +/usr/share/mysql +/usr/bin/mysql +/etc/logrotate.d/mysql +/etc/selinux/targeted/active/modules/100/mysql + +rm -rf /var/lib/mysql +rm -rf /var/lib/mysql/mysql +rm -rf /usr/local/mysql +rm -rf /usr/lib64/mysql +rm -rf /usr/share/mysql +rm -rf /usr/bin/mysql +rm -rf /etc/logrotate.d/mysql +rm -rf /etc/selinux/targeted/active/modules/100/mysql + +rm -rf /etc/my.cnf + +rpm -qa | grep -i mysql +``` + + + +# MySQL 客户端 + +执行脚本 + +```mysql +source <脚本绝对路径> +``` + +# 用户和权限 + +## 用户各项权限 + +```mysql +CREATE USER 'username'@'%' IDENTIFIED BY 'password'; +GRANT Usage ON *.* TO 'username'@'%'; +GRANT Alter ON database.* TO 'username'@'%'; +GRANT Create ON database.* TO 'username'@'%'; +GRANT Create view ON database.* TO 'username'@'%'; +GRANT Delete ON database.* TO 'username'@'%'; +GRANT Drop ON database.* TO 'username'@'%'; +GRANT Index ON database.* TO 'username'@'%'; +GRANT Insert ON database.* TO 'username'@'%'; +GRANT References ON database.* TO 'username'@'%'; +GRANT Select ON database.* TO 'username'@'%'; +GRANT Show view ON database.* TO 'username'@'%'; +GRANT Update ON database.* TO 'username'@'%'; +GRANT PROCESS ON *.* TO 'username'@'%'; +``` + +## 创建管理员用户 + +```mysql +create user 'zhaoyan'@'%' identified by 'zhaoyan@123'; +grant all on *.* to 'zhaoyan'@'%' with grant option; +flush privileges; +``` + +## 限制只能本地登录root + +```mysql +mysql -uroot -p +use mysql; +select * from user where user = 'root'; +update user set host = '127.0.0.1' where user = 'root' and host = '%'; +flush privileges; +``` + +## 修改root密码 + +```mysql +alter user 'root'@'localhost' IDENTIFIED BY '123456'; +``` + +## 允许远程登录 + +```mysql +grant all privileges on *.* to root@'%' identified by "123456"; +``` + +## 忘记密码重置 + +### Windows + +1. 首先停止服务 + 使用管理员用户打开CMD + + ```cmd + net stop MYSQL + ``` + + > MYSQL为MySQL数据库服务名称 + +2. 将MySQL 数据目录 C:\ProgramData\MySQL\MySQL Server 5.7 下的Data目录复制到 程序目录 C:\Program Files\MySQL\MySQL Server 5.7 下 + +3. 进入MySQL bin目录 + + ```cmd + cd "C:\Program Files\MySQL\MySQL Server 5.7\bin" + mysqld --skip-grant-tables + ``` + + > --skip-grant-tables 的意思是启动 MySQL 服务的时候跳过权限表认证 + +4. 重新打开一个cmd窗口,输入 mysql 回车 + ```cmd + cd "C:\Program Files\MySQL\MySQL Server 5.7\bin" + mysql + ``` + +5. 连接权限数据库:use mysql + +6. 修改数据库连接密码 + ```cmd + update user set authentication_string =password('123456') where user='root'; + flush privileges; + exit; + ``` + +7. 修改root 密码后,需要执行下面的语句和新修改的密码。不然开启 mysql 时会出错 + ```cmd + mysqladmin -u root -p shutdown + ``` + +8. 将程序目录 C:\Program Files\MySQL\MySQL Server 5.7 下的Data文件夹下复制到数据目录 C:\ProgramData\MySQL\MySQL Server 5.7 下 + +9. 重启 mysql + + ```cmd + net start mysql + ``` + +# 存储过程和函数 + +## 函数 + +创建函数要加上**DELIMITER $$**、**$$ DELIMITER** + +MySQL 8 还需要执行 + +```mysql +-- 查看该参数,默认为0 +select @@log_bin_trust_function_creators; +-- 设置为1 +set GLOBAL log_bin_trust_function_creators=1; +``` + +# 信息数据库(information_schema) + +## 查看某数据库中所有表的行数 + +```mysql +select table_name,table_rows from information_schema.tables +where TABLE_SCHEMA = 'qyqdb' +order by table_rows desc; +``` + +# 主从搭建 + +## 主节点配置 + +1. 修改/etc/my.cnf文件,并重启服务 + ```conf + [mysqld] + server-id=10 #服务器id (主从必须不一样) + log-bin=/var/lib/mysql/master10-bin #打开日志(主机需要打开) + + binlog-ignore-db=mysql #不给从机同步的库(多个写多行) + binlog-ignore-db=information_schema + binlog-ignore-db=performance_schema + binlog-ignore-db=sys + ``` + + 注意:log-bin等存储路径的配置,其父路径的属主和组必须是是mysql,且一般权限设置为777。 + + ​ 如果更改了mysql的存储目录,建议参考默认配置的目录,将新目录的属主和权限也做相应更改 + +2. 创建从节点访问用户(mysql上执行) + ```mysql + CREATE USER 'slave'@'10.181.110.11' IDENTIFIED BY 'slave.8888'; + GRANT REPLICATION SLAVE ON *.* TO 'slave'@'10.181.110.11'; + select user,host from mysql.user; + ``` + +3. 查看主节点状态(mysql上执行) + + ```mysql + systemctl restart mysql #重启服务 + show master status + ``` + +## 从节点配置 + +1. 修改/etc/my.cnf文件,并重启服务 + ```conf + server-id=11 + relay-log=relay-bin + read-only=1 + replicate-ignore-db=mysql # 不复制的库 + replicate-ignore-db=information_schema + replicate-ignore-db=performance_schema + replicate-ignore-db=sys + ``` + +2. 从库关联主库(mysql上执行) + + ```mysql + CHANGE MASTER TO MASTER_HOST='10.0.2.3', + MASTER_USER='slave', + MASTER_PASSWORD='slave.123456', + MASTER_LOG_FILE='master10-bin.000001', + MASTER_LOG_POS=154; + ``` + +3. 检查状态(mysql上执行) + + ```mysql + start slave; + show slave status\G + ``` + + 注意:# master_log_file 和 master_log_pos值为主库上面执行show master status得到 + + 如果 Slave_IO_Running 和 Slave_SQL_Running 都为 Yes,说明配置成功 + + \# 如果又更改了其他配置,重启服务后导致上面两个参数出现NO,可以重新执行步骤 2 + +## 同步故障 + +### Slave_SQL_Running:No + +**原因是主机和从机里的数据不一致** + +### Slave_IO_Running:Connecting + +**是因为从机使用你配置的主机信息没有登陆到主机里面!修改(从机里面)** + +```mysql +stop slave; +change master to master_host="192.168.106.133",master_port=3307,master_user="rep",master_password="123456",master_log_file="master.000001",master_log_pos=745; +start slave; +``` + +### Slave_IO_Running:No + +就是server-id 没有配置成功的原因,需要重新修改配置文件,复制配置文件到容器里面,然后重启就ok + +# 二进制日志文件(binlog) + +## 查看是否开启 + +```mysql +SHOW VARIABLES LIKE 'log_bin'; +``` + +## 配置 + +```mysql +[mysqld] +# 开启binlog +log_bin=ON +# binlog日志的基本文件名 +#log_bin_basename=/var/lib/mysql/mysql-bin +# binlog文件的索引文件, 管理所有binlog文件 +#log_bin_index=/var/lib/mysql/mysql-bin.index +# 配置serverid +server-id=1 +# 设置日志三种格式: STATEMENT、ROW、MIXED。 +binlog_format=mixed +# 设置binlog清理时间 +expire_logs_days=15 +# binlog每个日志文件大小 +max_binlog_size=100m +# binlog缓存大小 +binlog_cache_size=4m +# 最大binlog缓存大小 +max_binlog_cache_size=512m +``` + + + +# MyCat 2 + +## 安装 + +首先去**Gitee**代码仓库**clone**源码 + +```shell +git clone https://gitee.com/MycatOne/Mycat2.git +``` + +打开项目,注意jdk版本需要用**oracle jdk1.8**,否则没有**javafx**包 + +Maven下载依赖 + +修改父级`pom.xml`文件中的`<repository>`标签,http后加s + +```xml +<repository> + <id>mvnrepository</id> + <name>mvnrepository</name> + <url>https://www.mvnrepository.com/</url> +</repository> +``` + +执行`maven clean install -DskipTests` + +编译后的包位于根目录下的`mycat2\target\mycat2-1.22-release-jar-with-dependencies.jar` + +修改根目录下的`start.bat`批处理文件 + +```bat +"C:\Program Files\Java\jre1.8.0_202\bin\java" -Dfile.encoding=UTF-8 -DMYCAT_HOME=C:\Users\user\Downloads\Mycat2-v1.22-2020-6-25\mycat2\src\main\resources -jar C:\Users\user\Downloads\Mycat2-v1.22-2020-6-25\mycat2\target\mycat2-1.22-release-jar-with-dependencies.jar + +@REM java -Dfile.encoding=UTF-8 -DMYCAT_HOME=D:\newgit\f\mycat2\src\main\resources -jar +@REM D:\newgit\f\mycat2\target\mycat2-0.5-SNAPSHOP-20200110152640-single.jar +``` + +根据项目目录进行修改 + +## 配置 + +配置目录为根目录下的`mycat2\src\main\resources` + +### datasources + +```json +{ + "dbType": "mysql", + "idleTimeout": 60000, + "initSqls": [], + "initSqlsGetConnection": true, + "instanceType": "READ_WRITE", + "maxCon": 1000, + "maxConnectTimeout": 30000, + "maxRetryCount": 5, + "minCon": 1, + "name": "prototypeDs", + "password": "123456", + "type": "JDBC", + "url": "jdbc:mysql://localhost3306?useUnicode=true&serverTimezone=Asia/Shanghai&characterEncoding=UTF-8", + "user": "root", + "weight": 0 +} +``` + +## 启动 + +运行根目录下的`start.bat` + +## 命令 + +### 查看连接源 + +```mysql +/*+ mycat:showDataSources{} */ +``` + +### 添加数据源 + +```mysql +/*+ mycat:createDataSource{ + "dbType":"mysql", + "idleTimeout":60000, + "initSqls":[], + "initSqlsGetConnection":true, + "instanceType":"READ_WRITE", + "maxCon":1000, + "maxConnectTimeout":3000, + "maxRetryCount":5, + "minCon":1, + "name":"ds0", + "password":"123456", + "type":"JDBC", + "url":"jdbc:mysql://192.168.6.158:3306?useUnicode=true&serverTimezone=UTC&characterEncoding=UTF-8", + "user":"root", + "weight":0 +} */; +``` + +### 查看集群 + +```mysql +/*+ mycat:showClusters{} */ +``` + +### 添加集群 + +```mysql +/*+ mycat:createCluster{ + "clusterType":"MASTER_SLAVE", + "heartbeat":{ + "heartbeatTimeout":1000, + "maxRetry":3, + "minSwitchTimeInterval":300, + "slaveThreshold":0 + }, + "masters":[ + "ds0" //主节点 + ], + "maxCon":2000, + "name":"c0", + "readBalanceType":"BALANCE_ALL", + "switchType":"SWITCH" +} */; +``` + +### 创建库 + +```mysql +/*+ mycat:createSchema{ + "customTables":{}, + "globalTables":{}, + "normalTables":{}, + "schemaName":"xxf_sharding", + "shardingTables":{} +} */; +``` + +### 创建表 + +```mysql +/*+ mycat:createTable{ + "schemaName":"xxf_sharding", + "tableName":"xxf_user", + "shardingTable":{ + "createTableSQL":"CREATE TABLE `xxf_user` ( + `id` BIGINT(20) NOT NULL COMMENT '用户ID', + `user_name` VARCHAR(30) NULL DEFAULT NULL COMMENT '用户姓名', + `email` VARCHAR(50) NULL DEFAULT NULL COMMENT '用户邮箱', + `phone` VARCHAR(11) NULL DEFAULT NULL COMMENT '手机号码', + `sex` CHAR(1) NULL DEFAULT NULL COMMENT '用户性别', + PRIMARY KEY (`id`) USING BTREE + ) COMMENT='笑小枫-用户信息表' COLLATE='utf8_general_ci' ENGINE=InnoDB;", + + "function":{ + "properties":{ + "mappingFormat": "c${targetIndex}/xxf_sharding/xxf_user_${tableIndex}", + "dbNum":2, //分库数量 + "tableNum":3, //分表数量 + "tableMethod":"mod_hash(id)", //分表分片函数 + "storeNum":2, //实际存储节点数量 + "dbMethod":"mod_hash(id)" //分库分片函数 + } + }, + "partition":{ + } + } +} */; +``` + +## 分库分表 + +### 原理 + +一个数据库由很多表的构成,每个表对应着不同的业务,垂直切分是指按照业务将表进行分布到不同的数据库上面,这样也就将数据或者说压力分担到不同的库上面 + +### 垂直切分:分库 + +系统被拆分为用户、订单、支付多个模块,部署在不同机器上。 +分库的原则:由于跨库不能关联查询,所以有紧密关联的表应当放在一个数据库中,相互没有关联的表可以分不到不同的数据库。 + +### 水平切分:分表 + + + +### 常用的分片规则 + +1. 分片算法简介 + Mycat2支持常用的(自动)HASH型分片算法也兼容1.6的内置的(cobar)分片算法。 + HASH型分片算法默认要求集群名字以c为前缀,数字为后缀,`c0就是分片表第一个节点,c1就是第二个节点`,该命名规则允许用户手动改变。 + +2. mycat2与1.X版本区别 + mycat2的hash型分片算法多基于MOD_HASH,对应Java的%取余运算。 + mycat2的hash型分片算法对于值的处理,总是把分片值转换到列属性的数据类型再做计算。 + mycat2的hash型分片算法适用于等价条件查询。 + +3. 分片规则与适用性 + +4. | 分片算法 | 描述 | 分库 | 分表 | 数值类型 | + | ----------- | -------------- | ---- | ---- | -------------------------------------- | + | MOD_HASH | 取模哈希 | 是 | 是 | 数值,字符串 | + | UNI_HASH | 取模哈希 | 是 | 是 | 数值,字符串 | + | RIGHT_SHIFT | 右移哈希 | 是 | 是 | 数值 | + | RANGE_HASH | 两字段其一取模 | 是 | 是 | 数值,字符串 | + | YYYYMM | 按年月哈希 | 是 | 是 | DATE,DATETIME | + | YYYYDD | 按年月哈希 | 是 | 是 | DATE,DATETIME | + | YYYYWEEK | 按年周哈希 | 是 | 是 | DATE,DATETIME | + | HASH | 取模哈希 | 是 | 是 | 数值,字符串,如果不是,则转换成字符串 | + | MM | 按月哈希 | 否 | 是 | DATE,DATETIME | + | DD | 按日期哈希 | 否 | 是 | DATE,DATETIME | + | MMDD | 按月日哈希 | 是 | 是 | DATE,DATETIME | + | WEEK | 按周哈希 | 否 | 是 | DATE,DATETIME | + | STR_HASH | 字符串哈希 | 是 | 是 | 字符串 | + +#### 常用分片规则简介 + +##### MOD_HASH + +[数据分片]hash形式的分片算法。如果分片键是字符串,会将字符串hash转换为数值类型。 + +1. 分库键和分表键相同: +- 分表下标:分片值%(分库数量*分表数量) +- 分库下标:分表下表/分库数量 +2. 分库键和分表键相同: +- 分表下标:分片值%分表数量 +- 分库下标:分片值%分库数量 + +##### RIGHT_SHIFT + +[数据分片]hash形式的分片算法。仅支持数值类型。 +分片值右移两位,按分片数量取余。 + +##### YYYYMM + +[数值分片]hash形式的分片算法。仅用于分库。 +(YYYY*12+MM)%分库数量,MM为1–12。 + +##### MMDD + +仅用于分表。仅DATE、DATETIME类型。 +一年之中第几天%分表数。tbpartitions不能超过366。 + +# MySQL 查询语句 + +## 查询语句中字段别名取表中的comment + +在 MySQL 中,字段的 comment 并不存储在数据行里,而是放在 **information_schema.COLUMNS** 中。 +因此可以通过把 **column_comment** 查出来,再拼成 **SELECT 语句的别名**,实现“用表中字段的 comment 做别名”的效果。 + +一次性查出某张表所有列,并把 comment 作为别名: + +```mysql +-- 示例:表名 mydb.mytable +SELECT CONCAT( + 'SELECT ', + GROUP_CONCAT( + CONCAT('`', COLUMN_NAME, '` AS `', REPLACE(COLUMN_COMMENT, '`', ''), '`') + ORDER BY ORDINAL_POSITION + ), + ' FROM mytable;' + ) AS sql_text +FROM information_schema.COLUMNS +WHERE TABLE_SCHEMA = 'mydb' + AND TABLE_NAME = 'mytable'; +``` + +执行后得到一条 SQL,例如: + +```mysql +SELECT id AS 主键, name AS 用户名, age AS 年龄 FROM mytable; +``` + +**拼 SQL 时丢列** +上一段拼接脚本里 `GROUP_CONCAT` 有 **长度限制**(默认 1024 字节)。 +如果表列很多,会被截断导致看起来“少字段”。 +解决方法: + +```mysql +SET SESSION group_concat_max_len = 1000000; -- 临时放大 +``` + diff --git a/source/_posts/MySQL/image-20240130140632864.png b/source/_posts/MySQL/image-20240130140632864.png new file mode 100644 index 0000000..ad888f5 Binary files /dev/null and b/source/_posts/MySQL/image-20240130140632864.png differ diff --git a/source/_posts/MySQL/image-20240130140723465.png b/source/_posts/MySQL/image-20240130140723465.png new file mode 100644 index 0000000..8bd39fb Binary files /dev/null and b/source/_posts/MySQL/image-20240130140723465.png differ diff --git a/source/_posts/MySQL/image-20240130142057208.png b/source/_posts/MySQL/image-20240130142057208.png new file mode 100644 index 0000000..188d63d Binary files /dev/null and b/source/_posts/MySQL/image-20240130142057208.png differ diff --git a/source/_posts/MySQL/image-20240130142150299.png b/source/_posts/MySQL/image-20240130142150299.png new file mode 100644 index 0000000..248d917 Binary files /dev/null and b/source/_posts/MySQL/image-20240130142150299.png differ diff --git a/source/_posts/MySQL/image-20240130142408659.png b/source/_posts/MySQL/image-20240130142408659.png new file mode 100644 index 0000000..806f863 Binary files /dev/null and b/source/_posts/MySQL/image-20240130142408659.png differ diff --git a/source/_posts/MySQL/image-20240130142410127.png b/source/_posts/MySQL/image-20240130142410127.png new file mode 100644 index 0000000..806f863 Binary files /dev/null and b/source/_posts/MySQL/image-20240130142410127.png differ diff --git a/source/_posts/MySQL/image-20240130142551097.png b/source/_posts/MySQL/image-20240130142551097.png new file mode 100644 index 0000000..332678a Binary files /dev/null and b/source/_posts/MySQL/image-20240130142551097.png differ diff --git a/source/_posts/MySQL/image-20240130151257100.png b/source/_posts/MySQL/image-20240130151257100.png new file mode 100644 index 0000000..8ac8c36 Binary files /dev/null and b/source/_posts/MySQL/image-20240130151257100.png differ diff --git a/source/_posts/MySQL/image-20240130172202785.png b/source/_posts/MySQL/image-20240130172202785.png new file mode 100644 index 0000000..36eaa20 Binary files /dev/null and b/source/_posts/MySQL/image-20240130172202785.png differ diff --git a/source/_posts/MySQL/image-20240130172206916.png b/source/_posts/MySQL/image-20240130172206916.png new file mode 100644 index 0000000..36eaa20 Binary files /dev/null and b/source/_posts/MySQL/image-20240130172206916.png differ diff --git a/source/_posts/MySQL/zh-cn_image_0000001729034977.png b/source/_posts/MySQL/zh-cn_image_0000001729034977.png new file mode 100644 index 0000000..6be9b67 Binary files /dev/null and b/source/_posts/MySQL/zh-cn_image_0000001729034977.png differ diff --git a/source/_posts/Mybatis基本介绍.md b/source/_posts/Mybatis基本介绍.md new file mode 100644 index 0000000..cd310c1 --- /dev/null +++ b/source/_posts/Mybatis基本介绍.md @@ -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&useUnicode=true&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&useUnicode=true&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就行了,引用数据类型比如Integer,String 要把首字目小写,integer,string,这是因为用到了别名,也可以像实体类一样写java.lang.Integer,java.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语法,比如说and,or,where, + 此时可以使用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 是1,2,3,5符合条件的行 + +相当于 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}中传值 \ No newline at end of file diff --git a/source/_posts/NET.md b/source/_posts/NET.md new file mode 100644 index 0000000..c102ac0 --- /dev/null +++ b/source/_posts/NET.md @@ -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 包”时就可以看到并安装本地包了。 + diff --git a/source/_posts/Nginx.md b/source/_posts/Nginx.md new file mode 100644 index 0000000..580cdd5 --- /dev/null +++ b/source/_posts/Nginx.md @@ -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 Events(SSE),以确保数据立即传递给客户端而不进行缓冲。 + 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 +``` + diff --git a/source/_posts/Oracle.md b/source/_posts/Oracle.md new file mode 100644 index 0000000..a523f87 --- /dev/null +++ b/source/_posts/Oracle.md @@ -0,0 +1,1188 @@ +--- +title: Oracle +date: 2021-03-23 10:30:31 +author: 文永达 +--- +# Oracle Database + +## 安装Oracle 19c + +### Linux下安装 + +#### rpm方式 + +从Oracle官网下载安装包 Linux x86-64 RPM + +https://www.oracle.com/database/technologies/oracle-database-software-downloads.html + +![image-20240226135109613](Oracle/image-20240226135109613.png) + +安装帮助文档 + +https://docs.oracle.com/en/database/oracle/oracle-database/19/ladbi/preface.html#GUID-071A6B76-11E3-4421-963E-41DA6F2EF07A + +下载 `preinstall` 下载地址 + +https://yum.oracle.com/repo/OracleLinux/OL7/latest/x86_64/index.html,浏览器搜索-19c + +或者 + +```shell +curl -o oracle-database-preinstall-19c-1.0-1.el7.x86_64.rpm https://yum.oracle.com/repo/OracleLinux/OL7/latest/x86_64/getPackage/oracle-database-preinstall-19c-1.0-1.el7.x86_64.rpm +``` + +安装,首先执行 + +```shell +yum localinstall -y oracle-database-preinstall-19c-1.0-1.el7.x86_64.rpm +``` + +会提示缺少依赖 + +可以去RPM源网站下载 + +http://www.rpmfind.net/linux/rpm2html/search.php?query=compat-libcap1(x86-64) + +安装 + +```shell +rpm -ivh compat-libcap1-1.10-7.el7.x86_64.rpm +``` + +再次执行 + +```shell +yum localinstall -y oracle-database-preinstall-19c-1.0-3.el7.x86_64.rpm +``` + +安装数据库 + +```shell +yum localinstall -y oracle-database-ee-19c-1.0-1.x86_64.rpm +``` + +配置数据库 + +```shell +/etc/init.d/oracledb_ORCLCDB-19c configure +``` + +过程较长,等待即可 + +可能会JDK报错 + +```shell +yum install libnsl +``` + +配置完成后设置当前用户下的环境变量 + +```shell +vim /etc/profile.d/oracle19c.sh + +export ORACLE_HOME=/opt/oracle/product/19c/dbhome_1 +export PATH=$PATH:/opt/oracle/product/19c/dbhome_1/bin +export ORACLE_SID=ORCLCDB + +# 保存退出,执行 +source /etc/profile.d +``` +验证安装是否正确 + +```shell +passwd oracle + +su oracle + +sqlplus / as sysdba + +# 提示 +SQL*Plus: Release 19.0.0.0.0 - Production on Mon Oct 17 12:25:27 2022 +Version 19.3.0.0.0 + +Copyright (c) 1982, 2019, Oracle. All rights reserved. + +Connected to an idle instance. +``` + +启动监听 + + +![image-20221017123327552](http://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221017123327552.png) + +#### Docker方式 + +安装Docker + +```shell +sudo wget -qO- https://get.docker.com/ | bash +docker --version +systemctl start docker +systemctl status docker +systemctl enable docker +``` + +拉取镜像 `quay.io/maksymbilenko/oracle-12c` + +```shell +docker pull quay.io/maksymbilenko/oracle-12c +``` + +如果有本地镜像则使用 + +```shell +docker build -t quay.io/maksymbilenko/oracle-12c . +``` + +构建容器 + +```shell +# 首先创建本地目录 +mkdir /oracle/data +# 授予权限 +chmod -R 777 /oracle/data +docker run --name o12c -d -p 8080:8080 -p 1521:1521 -v /oracle/data:/u01/app/oracle quay.io/maksymbilenko/oracle-12c +# 查看日志 +docker logs -f # 字符串ID +``` + + + +![image-20221017145702216](http://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221017145702216.png) + +安装完成 + +数据库连接信息 + +```yaml +hostname: localhost +port: 1521 +sid: xe +service name: xe +username: system +password: oracle +``` + +进入容器修改账号密码设置 + +```shell +# 查看所有容器信息 +docker ps -a +docker exec -it [containerID] /bin/bash +# 切换成oracle用户 +su oracle +# 进入sqlplus +$ORACLE_HOME/bin/sqlplus / as sysdba + +SQL*Plus: Release 12.1.0.2.0 Production on Sun Aug 1 03:15:37 2021 + +Copyright (c) 1982, 2014, Oracle. All rights reserved. + + +Connected to: +Oracle Database 12c Standard Edition Release 12.1.0.2.0 - 64bit Production + +# 设置密码有效期为无限制 +SQL> ALTER PROFILE DEFAULT LIMIT PASSWORD_LIFE_TIME UNLIMITED; + +Profile altered. + +SQL> alter user SYSTEM account unlock; + +User altered. + +# 创建一个账号为act_test的用户密码设置为test +SQL> create user act_test identified by test; + +User created. +# 为这个用户赋予管理员的权限 +SQL> grant dba to act_test; + +Grant succeeded. + +# ctrl + p + q 退出容器(注意不要exit退出,防止容器直接关闭了) +``` + + + +### Windows下安装 + + + +## Oracle SQL Developer + +### 设置自动提示 + +工具栏 -> 工具 -> 首选项 -> 代码编辑器 -> 完成设置 + +![image-20221017111632399](http://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221017111632399.png) + +### 设置代码模板 + +工具栏 -> 工具 -> 首选项 -> 代码编辑器 -> 代码模板 + +![image-20221017112825105](http://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221017112825105.png) + +### 同时打开多个表 + +工具栏 -> 工具 -> 首选项 -> 数据库 ->对象查看器 + +![image-20221020141231505](http://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221020141231505.png) + +## SQL PLUS + +### 解决乱码 + +```shell +sqlplus / as sysdba + +col parameter for a30 +col value for a25 +select * from nls_database_parameters; + +PARAMETER VALUE +------------------------------ ------------------------- +NLS_RDBMS_VERSION 19.0.0.0.0 +NLS_NCHAR_CONV_EXCP FALSE +NLS_LENGTH_SEMANTICS BYTE +NLS_COMP BINARY +NLS_DUAL_CURRENCY $ +NLS_TIMESTAMP_TZ_FORMAT DD-MON-RR HH.MI.SSXFF AM + TZR + +NLS_TIME_TZ_FORMAT HH.MI.SSXFF AM TZR +NLS_TIMESTAMP_FORMAT DD-MON-RR HH.MI.SSXFF AM +NLS_TIME_FORMAT HH.MI.SSXFF AM + +PARAMETER VALUE +------------------------------ ------------------------- +NLS_SORT BINARY +NLS_DATE_LANGUAGE AMERICAN +NLS_DATE_FORMAT DD-MON-RR +NLS_CALENDAR GREGORIAN +NLS_NUMERIC_CHARACTERS ., +NLS_NCHAR_CHARACTERSET AL16UTF16 +NLS_CHARACTERSET AL32UTF8 +NLS_ISO_CURRENCY AMERICA +NLS_CURRENCY $ +NLS_TERRITORY AMERICA +NLS_LANGUAGE AMERICAN + +``` + +`NLS_LANG`的组成规则为 `NLS_LANGUAGE_NLS_TERRITORY.NLS_CHARACTERSET` + +```shell +vim ~/.bash_profile + +export NLS_LANG="AMERICAN_AMERICA.AL32UTF8" +``` + + + +### 解决控制台输错命令删除 + +使用`Ctrl + backspace`代替`backspace` + +### 登录 + +#### 使用操作系统认证 + +适用于以管理员身份登录数据库: + +```bash +sqlplus / as sysdba +``` + +- */* 表示操作系统认证。 +- *as sysdba* 用于以管理员权限登录。 + +#### 使用用户名和密码登录 + +通过提供用户名、密码和数据库连接信息: + +```bash +sqlplus username/password@hostname:port/SID +``` + +**示例:** + +```bash +sqlplus scott/tiger@192.168.1.100:1521/orcl +``` + +- *hostname* 是数据库主机名或 IP 地址。 +- *port* 是监听端口,默认是 *1521*。 +- *SID* 是数据库实例名。 + +如果已配置 TNS,则可以简化为: + +```bash +sqlplus username/password@TNSNAME +``` + +#### 无日志模式登录 + +先启动 SQL*Plus,再手动连接数据库: + +```bash +sqlplus /nolog +``` + +然后使用以下命令连接: + +```bash +conn username/password@hostname:port/SID +``` + +**优点:** 避免直接暴露用户名和密码。 + +#### 直接交互式登录 + +直接输入 *sqlplus*,按提示输入用户名和密码: + +```bash +sqlplus +``` + +**示例:** + +``` +请输入用户名: scott +输入口令: tiger +``` + + + +```shell +# 以oracle账号登录 +su oracle +$ORACLE_HOME/bin/sqlplus / as sysdba +``` + +### 修改sys密码 + +```shell +sqlplus /nolog +conn as sysdba +alter user sys identified by 123456; +``` + + + +## CDB 和 PDB + +![24221659-693602e2df62491e8cad466d5b865147](Oracle\24221659-693602e2df62491e8cad466d5b865147.gif) + +- CDB :容器数据库,名称为 CDB$ROOT。其作用就是系统数据库,sys账号等以及Common User(公共用户)都保存在里面。同时它可以管理PDB数据库 + +- PDB :可插拔的数据库。用户可以在PDB自建数据库 + - Oracle安装成功后有个默认的pdb数据库(在安装Oracle的时候自己设定) + - PDB中自带有PDB$SEED,属于PDB的模板数据库,自己创建数据库的时候以此库为模板,非常类似 SQL Server 中的 model 数据库 + +命令:如何查看当前的位置是CDB还是PDB使用sys登录,输入命令: + +```she +create pluggable database pdb1 admin user pdb1 identified by 1 file_name_convert=('/opt/oracle/oradata/ORCLCDB/pdbseed','/opt/oracle/oradata/ORCLCDB/pdb1'); -- 创建PDB,其中pdb1是我创建的可插接式数据库,pdb1是创建的用户,1是密码。file_name_convert是对应目录 + +show con_name; -- 查看当前所在容器位置 + +show pdbs; -- 查看所有的PDB + +alter pluggable database pdb1 open; -- 打开 pdb1 pdb + +alter pluggable database pdb1 close immediate; -- 立刻关闭 pdb1 + +alter pluggable database all open; -- 打开 所有 pdb + +alter session set container=cdb$root; -- pdb切换到cdb + +alter session set container=pdb1; -- cdb切换到pdb1 + +-- 查看 cdb、pdb 信息 +select name , cdb from v$database; + +select name,con_id from v$services; + +select name,con_id,open_mode from v$pdbs; +``` + +由于安装Oracle的时候设定PDB数据库为schooldb,故查询到两个PDB数据库 + +## 表空间 + +### 概述 + +1. 表空间 + 1. 表空间是一个逻辑的概念,真正存放数据的是数据文件(data files) + 2. 1 个数据库 = N 个表空间(N >= 1) + 1 个表空间 = N 个数据文件(dbf)(N >= 1) + -- 1个数据文件(dbf) 只能属于 1 个表空间 +2. 建立表空间的作用 + 1. 控制数据库占用 '磁盘空间' 的大小 + 2. 不同类型的数据存储到不同的位置,有利于提高 'I/O' 性能,同时有利于备份和恢复等操作 + +### 相关视图 + +```sql +-- 数据文件 +select * from dba_data_files; +select * from dba_temp_files; +-- 表空间 +select * from dba_tablespaces; +select * from dba_free_space; +-- 权限 +select distinct t.privilege + from dba_sys_privs t + where t.privilege like '%TABLESPACE%'; + + select t.tablespace_name, -- 表空间 + t.file_name, -- 文件名 + t.autoextensible, -- 是否自增 + t.bytes / 1024 / 1024 "SIZE(M)", -- 初始值 + t.increment_by * 8 / 1024 "NEXT(M)", -- 步长 1blok = 8KB + t.maxbytes / 1024 / 1024 "MAXSIZE(M)" -- 最大值 + from dba_data_files t; +``` + +![image-20240229151409259](Oracle/image-20240229151409259.png) + +### 语法 + +```sql +-- 表空间类型及名称,默认不指定类型(永久) +create [temporary | undo] tablespace "TBS" +-- 数据文件的位置及大小 +datafile 'D:\Oracle\TBS.dbf' size 10m +-- 是否自动扩展,默认 'off' +[autoextend off] | [autoextend on next n maxsize m] +-- 是否产生日志,默认 'logging' +[logging | nologging] +-- 段空间自动管理,默认 'auto' 推荐 +[segment space management auto] +-- 表空间管理方式,dictionary | local(默认,推荐) +[extent management local [uniform size n]] +``` + +- **创建一个永久表空间 “TBS01”,其大小为 10MB** + + ```sql + create tablespace "TBS01" + datafile 'D:\Oracle\TBS01.dbf' size 10m; + -- 1.路径必须存在,否则报错! + -- 2.表空间名称默认大写,除非用引号注明,如 "tbs" 则为小写 + ``` + +- **创建一个自增表空间 “TBS02”,其大小为 10MB,每次扩展 1MB,最大扩展到 20MB** + + ```sql + create tablespace "TBS02" + datafile 'D:\Oracle\TBS02.dbf' size 10m + autoextend on next 1m maxsize 20m; + ``` + +- **每个用户都有一个默认临时表空间,在创建用户时如果没指定将使用oracle 数据库设置的默认临时表空间,查询方法是:** + + ```sql + select property_name,property_value from database_properties where property_value=‘TEMP’ + ``` + +### 新建 + +```sql +CREATE TABLESPACE ACTERP_BD_DEV + LOGGING + DATAFILE + '/u01/app/oracle/oradata/orcl/acterp_bd_dev.dbf' SIZE 2048m AUTOEXTEND ON NEXT 50m MAXSIZE 20480m + EXTENT MANAGEMENT LOCAL; +-- 临时表空间 +CREATE TEMPORARY TABLESPACE ACTERP_BD_DEV_TEMP + TEMPFILE + '/u01/app/oracle/oradata/orcl/acterp_bd_dev_temp.dbf' SIZE 2048m AUTOEXTEND ON NEXT 50m MAXSIZE 20480m + EXTENT MANAGEMENT LOCAL; +``` + +### 查询 + +```sql +-- 查询表空间及对应数据文件 +select tablespace_name,file_id,bytes/1024/1024,file_name from dba_data_files order by file_id; +``` + + + +### 修改 + +```sql +-- 1 修改数据文件的大小为 20M +alter database datafile 'D:\Oracle\TBS01.dbf' +resize 20m; + +-- 2 修改数据文件为自动扩展,最大值为 1G +alter database datafile 'D:\Oracle\TBS01.dbf' +autoextend on next 20m maxsize 1g; + +-- 3 新增数据文件 +alter tablespace "TBS01" +add datafile 'D:\Oracle\TBS01_1.dbf' +size 200m; + +``` + +### 删除 + +```sql + +drop user acterp_pre cascade; + +drop tablespace acterp_pre including contents and datafiles cascade constraint; + +drop tablespace acterp_pre_temp including contents and datafiles cascade constraint; +``` + + + +## 用户 + +### 操作 + +#### cdb + +##### 新建 + +```sql +CREATE TABLESPACE ACT_DEV + DATAFILE + '/opt/oracle/oradata/ORCLCDB/act_dev.dbf' SIZE 100M AUTOEXTEND ON NEXT 100M MAXSIZE UNLIMITED LOGGING EXTENT MANAGEMENT LOCAL SEGMENT SPACE MANAGEMENT AUTO; + +create user C##act_dev identified by 123456 default tablespace ACT_DEV; + +grant dba,connect to C##act_dev; + +commit; +``` + +##### 删除 + +```sql +drop user pdb1 cascade; +#cascade 删除pdb1这个用户的同时,级联删除 pdb1 用户下的所有数据对象,如table等 +``` + +修改用户密码 + +```sql +alter user pdb1 identified by 1; +``` + + + +#### pdb + +```sql +# 首先切换到pdb +alter session set container=ORCLPDB1; -- cdb切换到ORCLPDB1 +# 创建用户名为 pdb1 密码为 1 的用户 +create user pdb1 identified by 1; + +grant create session to pdb1; +grant create table to pdb1; +grant create tablespace to pdb1; +grant create view to pdb1; +grant connect,resource to pdb1; + +grant dba to pdb1; +``` + + + +#### non-cdb + +```sql +-- 查看表空间及数据文件使用 +select tablespace_name,file_id,bytes/1024/1024 || 'm' as file_size,file_name from dba_data_files order by file_id; + +CREATE TABLESPACE ACTERP_PRE + LOGGING + DATAFILE + 'D:\APP\ORACLE\ORADATA\ORCL\acterp_pre.dbf' SIZE 2048m AUTOEXTEND ON NEXT 50m MAXSIZE 20480m + EXTENT MANAGEMENT LOCAL; + +CREATE TEMPORARY TABLESPACE ACTERP_PRE_TEMP + TEMPFILE + 'D:\APP\ORACLE\ORADATA\ORCL\acterp_pre_temp.dbf' SIZE 2048m AUTOEXTEND ON NEXT 50m MAXSIZE 20480m + EXTENT MANAGEMENT LOCAL; + +create user acterp_pre identified by 1 default tablespace ACTERP_PRE temporary tablespace ACTERP_PRE_TEMP; + +grant create session to acterp_pre; +grant create table to acterp_pre; +grant create tablespace to acterp_pre; +grant create view to acterp_pre; +grant connect,resource to acterp_pre; + +grant dba to acterp_pre; + +commit; +``` + + + +## 语法 + +### 新建表空间 + +```sql +CREATE TABLESPACE ACT_DEV + DATAFILE + '\oracle\data\oradata\xe\FILE_SPECIFICATION1.dbf' SIZE 52428800 AUTOEXTEND ON NEXT 52428800 MAXSIZE 2147483648 + + EXTENT MANAGEMENT LOCAL; +``` + +### 解除占用 + +```sql +select l.session_id,o.owner,o.object_name +from v$locked_object l,dba_objects o +where l.object_id=o.object_id; + +SELECT sid, serial#, username, osuser FROM v$session where sid = sid; + +alter system kill session 'sid,serial#'; +``` + +### 修改表 + +```sql +-- 表重命名 +ALTER TABLE BOOK +RENAME TO BIND_PHONE_NUMBER; +-- 添加表字段Column +ALTER TABLE BIND_PHONE_NUMBER +ADD (USERNAME VARCHAR2(20) ); +-- 修改表字段Column名 +ALTER TABLE BIND_PHONE_NUMBER RENAME COLUMN NAME TO APPNAME; +``` + +### 使用关键字做完表名,列名 + +使用双引号""形式,如"INDEX" + +### 删除表数据 + +```sql +TRUNCATE TABLE 表名 +-- or +DELETE FROM 表名 +``` + +### 从其他表中复制数据到插入一张表中 + +```sql +-- 标准语法 +INSERT INTO table2 +SELECT * FROM table1; +-- 多表插入到一张表 示例,ID为GUID, +-- 需要注意的是如果指定插入到哪些列中,不是根据后面SELECT的列的别名来插入,而是通过列的顺序插入,语句后可接WHERE条件 +INSERT INTO table1(ID,NAME,TEXT) SELECT SYS_GUID(), t2.NAME, t3.TEXT FROM DUAL, TABLE2 t2, TABLE3 t3; + +``` + +### directory目录 + +```sql +-- 查询directory目录 +select * from dba_directories; +-- 创建或者修改 directory目录 +create or replace directory dum_date_dir as '/home/oracle/datatmp' +-- 赋权 directory目录 +ant read,write on directory dumpdir to username; +-- 删除directory目录 +drop directory DIRENAME; + +``` + + + +## 数据泵 + +10g开始引入了数据泵(Data Dump)技术,可以快速将数据库元数据和数据快速移动到另一个oracle数据库中 + +### 导入 impdp + +```shell +impdp acterp_bd_dev/1@ORCLCDB REMAP_SCHEMA = acterp_bd_dev:acterp_bd_dev table_exists_action = replace directory=data_pump_dir dumpfile=acterp_bd_dev.dmp logfile=impdp_acterp_bd_dev.log +``` + +如果是`non-cdb`需去掉`@SID` + +## 内连接与外连接 + +### 内连接 + +合并具有同一列的两个以上的表的行,结果集中不包含一个表与另一个表不匹配的行 + +语法: + +```sql +SELECT 字段列表 +FROM A表 INNER JOIN B表 +ON 关联条件 +WHERE 条件; +``` + +类似于: + +```sql +方式一 +SELECT e.employee_id, e.last_name, e.department_id, +d.department_id, d.location_id +FROM employees e JOIN departments d +ON (e.department_id = d.department_id); + +方式二: +SELECT employee_id,department_name +FROM employees e,departments d +WHERE e.`department_id` = d.department_id; +``` + +这种查询方式,它会把所有的符合where条件的字段查询出来。但是有这样一种情况,就是两张表的数据有的不存在某种关系。 + +缺点:如果我们想要把不满足条件的数据也查询出来,内连接就做不到。 + +于是引入外连接。 + +### 外连接 + +查询多表时一般要求中出现:查询所有的数据时,就一定会用到外连接。 + +两个表在连接过程中除了返回满足连接条件的行以外还返回左(或右)表中不满足条件的行,这种连接称为左(或右)外连接。没有匹配的行时,结果表中相应的列为空(NULL)。 + +#### 满外连接 + +`FULL JOIN` + +`LEFT JOIN UNION RIGHT JOIN` + +#### 左外连接 + +语法: + +```sql +SELECT 字段列表 +FROM A表 LEFT JOIN B表 +ON 关联条件 +WHERE 条件; +``` + +类似于: + +```sql +SELECT e.last_name, e.department_id, d.department_name +FROM employees e +LEFT OUTER JOIN departments d +ON (e.department_id = d.department_id) ; +``` + +`employees`表中的数据会全部显示出来 + +#### 右外连接 + +语法: + +```sql +SELECT 字段列表 +FROM A表 RIGHT JOIN B表 +ON 关联条件 +WHERE 条件; +``` + +类似于: + +```sql +SELECT e.last_name, e.department_id, d.department_name +FROM employees e +RIGHT OUTER JOIN departments d +ON (e.department_id = d.department_id) ; +``` + +`departments`表中的数据会全部显示出来 + +### UNION的使用 + +·语法: + +```sql +SELECT column,... FROM table1 +UNION [ALL] +SELECT column,... FROM table2 +``` + +UNION 操作符返回两个查询的结果集的并集,去除重复记录。 + +UNION ALL操作符返回两个查询的结果集的并集。对于两个结果集的重复部分,不去重。 + +## Oracle 函数 + +### NVL() + +![image-20230228133334777](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20230228133334777.png) + +```sql +SELECT a.OSPREQID,a.OSPREQNO FROM T_OSP_REQ a,T_OSP_REQDETAIL b WHERE a.OSPREQID = b.OSPREQID AND b.OSPNO IN (SELECT OSPNP FROM T_BPM_OSP WHERE OSPNO IN ('OSP202302280002')) AND NVL(DATASTATUS, ' ')<>'撤销' +``` + +如果**DATASTATUS**为**NULL**,则返回**' '**,否则返回**DATASTATUS** + +官方解释 + +The Oracle NVL () function allows you to **replace null** with a more meaningful alternative in the results of a query. The following shows the syntax of the NVL () function: The NVL () function accepts two arguments. If e1 evaluates to null, then NVL () function returns e2. If e1 evaluates to non-null, the NVL () function returns e1. + +Oracle NVL()函数允许您在查询结果中用更有意义的替代项替换NULL。下面显示了NVL()函数的语法:NVL()函数接受两个参数。如果e1的计算结果为空,则NVL()函数返回e2。如果e1的计算结果为非空,则nvl()函数返回e1。 + +### DECODE() + +用法 DECODE(表达式, 条件1,返回值1,条件2,返回值2) + +```sql +SELECT DECODE(AMOUNT, 0, NULL, AMOUNT) FROM T_PO_ORDERDETAIL; +``` + +如果**AMOUNT**等于**0**,则返回**NULL**,否则返回**AMOUNT** + +```sql +SELECT DECODE(AMOUNT, 0, NULL, 1, 1, AMOUNT) FROM T_PO_ORDERDETAIL; +``` + +如果**AMOUNT**等于**0**,则返回**NULL**,否则如果AMOUNT等于1,则返回1,否则返回**AMOUNT** + +### DECODE替换NVL + +在Oracle中,DECODE函数通常可以替换使用NVL函数。DECODE函数可以在字段值满足多个条件时返回不同的结果值,语法如下: + +``` +DECODE(expr, search, result, default) +``` + +其中,expr是要进行条件判断的表达式,search是需要匹配的条件值,result是匹配成功后返回的结果值,default是在没有匹配成功时返回的默认值。 + +使用DECODE函数来替换NVL函数的示例如下: + +使用NVL函数处理NULL值: + +``` +SELECT NVL(name, '未知') AS name FROM user; +``` + +使用DECODE函数替换NVL函数: + +``` +SELECT DECODE(name, NULL, '未知', name) AS name FROM user; +``` + +以上语句中,使用DECODE函数将name参数的NULL值替换为“未知”字符串。当name不为NULL时,返回它本身的值。 + +### LTRIM + +ltrim(char[,set]) + +去掉字符串 char 左侧包含在 set 中的任何字符,直到第一个不在 set 中出现的字符为止 + +### RTRIM + +rtrim(char[,set]) + +去掉字符串 char 右侧包含在 set 中的任何字符,直到第一个不在 set 中出现的字符为止 + +```sql +SELECT ltrim('abcd','a') lefttrim, rtrim('abcde','e') righttrim FROM dual; + +LEFTTRIM RIGHTTRIM +-------- --------- +bcd abcd +``` + + + +## 特性 + +### Row Movement + +ROW MOVEMENT特性最初是在8i时引入的,其目的是提高分区表的灵活性——允许更新Partition Key。这一特性默认是关闭,只是在使用到一些特殊功能时会要求打开。除了之前提到的更新Partition Key,还有2个要求打开的ROW MOVEMENT的功能就是flushback table和Shrink Segment。 + +先看Flashback Table。这一功能能帮助我们及时回滚一些误操作,防止数据意外丢失。在使用该功能之前,必须先打开ROW MOVEMENT,否则就会抛ORA-08189错误。我们看以下例子,可以说明在使用Flashback Table功能时,ROW MOVEMENT产生了什么作用: + +当开启ROW MOVEMENT后,表被顺利的flashback了,数据被找回。此时,再比较flashback前后记录的ROWID,大多数记录的物理位置都变化。这个过程的内部操作, 可以通过对Flashback Table做SQL Trace来进一步观察。 + +通过Trace,我们不难发现,Flashback Table实际是通过Flashback Query将表中数据进行了一次删除、插入操作,因此ROWID会发生变化。 + + + +在更新记录中的Partition Key时,可能会导致该记录超出当前所在分区的范围,需要将其转移到其他对应分区上,因此要求开启ROW MOVEMENT。 + +这一操作产生影响的特殊之处在于这是个DML操作,是和online transaction密切相关。对于这样一个UPDATE,实际上分为3步:先从原有分区将数据删除;将原数据转移到新分区上;更新数据。 + +其影响就在于以下几个方面: + +- 一个UPDATE被分解为DELET、INSERT、UPDATE三个操作,增加了性能负担。其中,DELETE的查询条件与原UPDATE的查询条件相同,新的UPDATE的查询条件是基于INSERT生成的新的ROWID; + +- 相应的Redo Log、Undo Log会增加; + +- 如果Update语句还涉及到了Local Index的字段的话,新、旧2个分区上的Local Index都要被更新。 + +## 分区表 + +### 范围分区 + +```sql +create tablespace tetstbs1 datafile '/opt/oracle/oradata/ORCLCDB/tetstbs1.dbf' size 1m autoextend on next 5m maxsize unlimited; +create tablespace tetstbs2 datafile '/opt/oracle/oradata/ORCLCDB/tetstbs2.dbf' size 1m autoextend on next 5m maxsize unlimited; +create tablespace tetstbs3 datafile '/opt/oracle/oradata/ORCLCDB/tetstbs3.dbf' size 1m autoextend on next 5m maxsize unlimited; + +-- 范围分区 +create table pt_range_test1( + pid number(10), + pname varchar2(30) +) partition by range(pid)( +-- 分区 p1 pid值小于 1000 表空间 tetstbs1 + partition p1 values less than(1000) tablespace tetstbs1, +-- 分区 p2 pid值小于 2000 表空间 tetstbs2 + partition p2 values less than(2000) tablespace tetstbs2, +-- 分区 p3 pid值小于 number最大值 tetstbs3 + partition p3 values less than(maxvalue) tablespace tetstbs3 +) enable row movement; + +insert into pt_range_test1 (pid, pname) values (1, '瑶瑶'); +insert into pt_range_test1 (pid, pname) values (1500, '倩倩'); +insert into pt_range_test1 (pid, pname) values (null, '优优'); +commit; + +select * from user_tab_partitions t; +select 'P1' 分区名, t.* from pt_range_test1 partition (p1) t union all +select 'P2' 分区名, t.* from pt_range_test1 partition (p2) t union all +select 'P3' 分区名, t.* from pt_range_test1 partition (p3) t; + +select 'P1' 分区名, t.* from pt_range_test1 PARTITION (p1) t; + +select t.* from pt_range_test1 PARTITION (p1) t; +select t.* FROM pt_range_test1 t; +``` + + + +### 列表分区 + +```sql +-- 列表分区 +create table pt_list_test( + pid number(10), + pname varchar2(30), + sex varchar2(10) +) partition by list(sex)( + partition p1 values ('MAN', '男') tablespace tetstbs1, + partition p2 values ('WOMAN', '女') tablespace tetstbs2, + partition p3 values (default) tablespace tetstbs3 +) enable row movement; + +insert into pt_list_test (pid, pname, sex) values (1, '瑶瑶', '男'); +insert into pt_list_test (pid, pname, sex) values (2, '倩倩', 'WOMAN'); +insert into pt_list_test (pid, pname, sex) values (3, '优优', 'GOD'); +insert into pt_list_test (pid, pname, sex) VALUES (4, '雨雨', '女'); +insert into pt_list_test (pid, pname, sex) VALUES (5, '闫闫', 'MAN'); +commit; + +update pt_list_test set sex = '男' where pid = 1; +update pt_list_test set sex = '女' where pid = 1; + +select 'p1' 分区名, t.*, rowid from pt_list_test PARTITION (p1) t UNION all +select 'p2' 分区名, t.*, rowid from pt_list_test PARTITION (p2) t UNION all +select 'p3' 分区名, t.*, rowid from pt_list_test PARTITION (p3) t; +``` + + + +### 哈希分区 + +```shell +create tablespace tetstbs4 datafile '/opt/oracle/oradata/ORCLCDB/tetstbs4.dbf' size 1m autoextend on next 5m maxsize unlimited; +-- 哈希分区 +create table pt_hash_test( + pid number(10), + pname varchar2(30) +) partition by hash(pid)( + partition p1 tablespace tetstbs1, + partition p2 tablespace tetstbs2, + partition p3 tablespace tetstbs3, + partition p4 tablespace tetstbs4 +); +-- 简写 +create table pt_hash_test2( + pid number(10), + pname varchar2(30) +) partition by hash(pid) + partitions 4 store in (tetstbs1, tetstbs2, tetstbs3, tetstbs4); + +insert into pt_hash_test (pid, pname) values (1, '瑶瑶'); +insert into pt_hash_test (pid, pname) values (1500, '倩倩'); +insert into pt_hash_test (pid, pname) values (null, '优优'); +insert into pt_hash_test (pid, pname) values (2000, '闫闫'); +commit; + +select * from user_tab_partitions t; +select 'P1' 分区名, t.* from pt_hash_test partition (p1) t union all +select 'P2' 分区名, t.* from pt_hash_test partition (p2) t union all +select 'P3' 分区名, t.* from pt_hash_test partition (p3) t UNION all +SELECT 'p4' 分区名, t.* from pt_hash_test PARTITION (p4) t; + +select 'P1' 分区名, t.* from pt_hash_test PARTITION (p1) t; + +select t.* from pt_hash_test PARTITION (p1) t; +select t.* FROM pt_hash_test t; +``` + + + +### 组合分区 + +#### 范围列表 + +```sql +create table pt_range_list_test( + pid number(10), + pname varchar2(30), + sex varchar2(10), + create_date date +) partition by range(create_date) + subpartition by list(sex)( + partition p1 values less than(to_date('2020-01-01', 'YYYY-MM-DD')) tablespace tetstbs1( + subpartition sub1p1 values('MAN') tablespace tetstbs1, + subpartition sub2p1 values('WOMAN') tablespace tetstbs1, + subpartition sub3p1 values(default) tablespace tetstbs1 + ), + partition p2 values less than(to_date('2021-01-01', 'YYYY-MM-DD')) tablespace tetstbs2( + subpartition sub1p2 values('MAN') tablespace tetstbs2, + subpartition sub2p2 values('WOMAN') tablespace tetstbs2, + subpartition sub3p2 values(default) tablespace tetstbs2 + ), + partition p3 values less than(maxvalue) tablespace tetstbs3( + subpartition sub1p3 values('MAN') tablespace tetstbs3, + subpartition sub2p3 values('WOMAN') tablespace tetstbs3, + subpartition sub3p3 values(default) tablespace tetstbs3 + ) + ) enable row movement; + +insert into pt_range_list_test (pid, pname, sex, create_date) values(1, '瑶瑶', 'WOMAN', to_date('2019-02-02', 'YYYY-MM-DD')); +insert into pt_range_list_test (pid, pname, sex, create_date) values(2, '闫闫', 'MAN', to_date('2020-06-21', 'YYYY-MM-DD')); +insert into pt_range_list_test (pid, pname, sex, create_date) values(3, '雨雨', 'WOMAN', TO_DATE('2022-04-03', 'YYYY-MM-DD')); +commit; +delete from pt_range_list_test where pid = 1; +select 'p1' 分区名, t.* from pt_range_list_test PARTITION (p1) t UNION all +select 'p2' 分区名, t.* from pt_range_list_test PARTITION (p2) t UNION all +select 'p3' 分区名, t.* from pt_range_list_test PARTITION (p3) t; +``` + + + +### 自动分区 + +在Oracle Database 12.2 之前,如果使用列表分区,当插入的数据超过了分区列表值设定,则会抛出异常;而如果存在大量的列表值需要定义,则可能需要一一设置。 + +在12.2引入的新特性中 - Auto-List Partitioning 可以针对新的列表值,进行自动的分区创建,从而减少了维护的复杂性。 + +```sql +drop table enmotech purge; + +CREATE TABLE enmotech ( + PartID integer not null, + CretTm date not null, + PartCD varchar2(2) not null +) partition by list (partcd) automatic ( + partition pBJ values ('BJ'), + partition pCD values ('CD'), + partition pGZ values ('GZ'), + partition pSH values ('SH') +); + +``` + +如果这个自动分片的分区名不符合你的命名规则,可以通过DDL语句去修改变更 + +```sql +alter table enmotech rename partition SYS_P290 to pKM; +``` + +对于已有的分区定义,可以通过关键字 automatic 和 manual 来进行分区定义的调整 + +```sql +alter table PEOPLE set partitioning automatic; + +alter table PEOPLE set partitioning manual; +``` + +### 间隔分区 + +## SQL 优化 + +### 什么是解释计划? + +解释计划是一个关于SQL查询语句执行过程的文本表示。它显示了Oracle数据库如何执行查询语句,并给出了每个步骤的详细信息,如表的使用方式、索引的使用情况、连接的方法等等。通过分析解释计划,我们可以了解查询语句的执行情况,找出性能瓶颈,并针对性地进行优化。 + +### Execute Explain Plan的使用方法 + +在Oracle [SQL](https://deepinout.com/sql/sql-top-tutorials/1694067222_j_sql-tutorial.html) Developer中,我们可以使用”Execute Explain Plan”来生成查询语句的解释计划。首先,我们需要打开SQL Worksheet,并输入要分析的查询语句。然后,在工具栏中选择”Explain Plan”按钮,或者使用快捷键Ctrl+E来执行解释计划。在执行过程中,Oracle数据库会为查询语句生成一个执行计划,并将其显示在输出窗口中。 + +下面是一个示例查询语句和其对应的解释计划: + +```sql +SELECT e.employee_id, e.last_name, d.department_name +FROM employees e, departments d +WHERE e.department_id = d.department_id +AND e.salary > 5000; +``` + +解释计划: + +| ID | 操作 | 名称 | 行数 | 成本 | +| :--: | :--------------: | :------------: | :--: | :--: | +| 0 | SELECT STATEMENT | | 5 | | +| 1 | NESTED LOOPS | | 5 | 2 | +| 2 | TABLE ACCESS | EMPLOYEES | 5 | 1 | +| 3 | INDEX | PK_EMPLOYEES | 107 | 2 | +| 4 | TABLE ACCESS | DEPARTMENTS | 1 | 1 | +| 5 | INDEX | PK_DEPARTMENTS | 27 | 1 | + +上面的解释计划使用表格的形式展示了查询语句的执行过程。每一列的含义如下: + +- ID:表示一个操作的唯一标识符 +- 操作:表示每个操作的名称,比如SELECT STATEMENT、NESTED LOOPS等 +- 名称:表示该操作对应的表的名称或索引的名称 +- 行数:表示该操作返回的结果行数 +- 成本:表示执行该操作需要的成本,成本越低表示执行速度越快 + +解释计划的每一行表示一个操作,操作之间的关系通过ID进行连接。在上面的示例中,ID为1的操作是一个NESTED LOOPS操作,它通过对EMPLOYEES表和DEPARTMENTS表进行连接来返回满足条件的结果。ID为2和ID为4的操作分别是对EMPLOYEES表和DEPARTMENTS表进行访问的操作,而ID为3和ID为5的操作则是对EMPLOYEES表和DEPARTMENTS表的主键索引进行访问的操作。 + +### 解释计划中常见的操作和符号 + +在解释计划中,我们会经常遇到一些常见的操作和符号。下面列举了一些常见的操作和符号,并对其进行了解释: + +- SELECT STATEMENT:表示整个查询语句的执行计划 +- TABLE ACCESS FULL:表示对表进行全表扫描,即读取表中的所有行 +- TABLE ACCESS BY INDEX ROWID:表示通过ROWID访问表,ROWID是一种唯一标识表中每行的标识符 +- INDEX RANGE SCAN:表示对索引进行范围扫描,即根据索引中的值范围来获取满足条件的结果 +- NESTED LOOPS:表示通过嵌套循环来执行连接操作 +- HASH JOIN:表示通过散列连接来执行连接操作,通常用于连接大量数据的情况 +- SORT JOIN:表示对结果进行排序后再执行连接操作 + +在实际情况中,我们可能会遇到更复杂的操作和符号,但通过对常见操作和符号的理解,我们可以初步了解查询语句的执行过程。 + +### 性能优化和解释计划 + +解释计划是优化SQL查询语句性能的有力工具。通过分析解释计划,我们可以找出查询语句执行过程中的瓶颈,并进行优化。下面是一些常见的优化方法: + +- 对表添加索引:如果解释计划中出现了全表扫描的操作,说明查询语句没有使用到索引。在这种情况下,我们可以通过添加索引来提高查询性能。 +- 优化连接操作:如果解释计划中出现了嵌套循环或Hash连接等操作,说明连接操作的性能较低。在这种情况下,我们可以考虑重新设计查询语句或调整连接顺序来提高性能。 +- 避免排序操作:如果解释计划中出现了排序操作,说明查询语句需要对结果进行排序。在这种情况下,我们可以考虑调整查询语句或添加索引来避免排序操作。 + +通过不断优化查询语句,我们可以提高数据库的查询性能,提升应用程序的响应速度。 + diff --git a/source/_posts/Oracle/24221659-693602e2df62491e8cad466d5b865147.gif b/source/_posts/Oracle/24221659-693602e2df62491e8cad466d5b865147.gif new file mode 100644 index 0000000..92437f6 Binary files /dev/null and b/source/_posts/Oracle/24221659-693602e2df62491e8cad466d5b865147.gif differ diff --git a/source/_posts/Oracle/image-20240226135109613.png b/source/_posts/Oracle/image-20240226135109613.png new file mode 100644 index 0000000..4054bfe Binary files /dev/null and b/source/_posts/Oracle/image-20240226135109613.png differ diff --git a/source/_posts/Oracle/image-20240229151409259.png b/source/_posts/Oracle/image-20240229151409259.png new file mode 100644 index 0000000..696eee2 Binary files /dev/null and b/source/_posts/Oracle/image-20240229151409259.png differ diff --git a/source/_posts/Podman.md b/source/_posts/Podman.md new file mode 100644 index 0000000..3571440 --- /dev/null +++ b/source/_posts/Podman.md @@ -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 + diff --git a/source/_posts/PostgreSQL.md b/source/_posts/PostgreSQL.md new file mode 100644 index 0000000..f103f84 --- /dev/null +++ b/source/_posts/PostgreSQL.md @@ -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 + diff --git a/source/_posts/PowerDesigner.md b/source/_posts/PowerDesigner.md new file mode 100644 index 0000000..8fe9313 --- /dev/null +++ b/source/_posts/PowerDesigner.md @@ -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],这里默认是让名称和代码同步,将前面的复选框去掉就行了。 + diff --git a/source/_posts/PowerShell.md b/source/_posts/PowerShell.md new file mode 100644 index 0000000..8afb823 --- /dev/null +++ b/source/_posts/PowerShell.md @@ -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)提示,输入管理员凭据确认。 diff --git a/source/_posts/PowerShell/image-20250718112238088.png b/source/_posts/PowerShell/image-20250718112238088.png new file mode 100644 index 0000000..c05a475 Binary files /dev/null and b/source/_posts/PowerShell/image-20250718112238088.png differ diff --git a/source/_posts/PowerShell/image-20250718142545538.png b/source/_posts/PowerShell/image-20250718142545538.png new file mode 100644 index 0000000..98d205a Binary files /dev/null and b/source/_posts/PowerShell/image-20250718142545538.png differ diff --git a/source/_posts/PowerShell/image-20250718142556458.png b/source/_posts/PowerShell/image-20250718142556458.png new file mode 100644 index 0000000..7c0d0da Binary files /dev/null and b/source/_posts/PowerShell/image-20250718142556458.png differ diff --git a/source/_posts/PowerShell/image-20250718142611106.png b/source/_posts/PowerShell/image-20250718142611106.png new file mode 100644 index 0000000..bcbba19 Binary files /dev/null and b/source/_posts/PowerShell/image-20250718142611106.png differ diff --git a/source/_posts/Pug.md b/source/_posts/Pug.md new file mode 100644 index 0000000..e63fc5d --- /dev/null +++ b/source/_posts/Pug.md @@ -0,0 +1,5 @@ +--- +title: Pug +date: 2025-05-19 13:20:34 +tags: +--- diff --git a/source/_posts/Python.md b/source/_posts/Python.md new file mode 100644 index 0000000..e185165 --- /dev/null +++ b/source/_posts/Python.md @@ -0,0 +1,108 @@ +--- +title: Python +date: 2025-03-10 14:26:30 +tags: +--- + +# pip + +## 查看版本 + +```shell +pip --version +``` + +## 使用Pip安装Github上的软件包 + +接下来,使用以下命令来安装Github上的软件包: + +```python +pip install git+https://github.com/username/repository.git +``` + +## 升级和卸载软件包 + +要升级软件包,可以使用以下命令: + +```python +pip install --upgrade package_name +``` + +其中,`package_name`是你要升级的软件包的名称。Pip会自动检查版本并安装最新的软件包。 + +如果你想卸载已安装的软件包,可以使用以下命令: + +```python +pip uninstall package_name +``` + +Pip会询问你是否确定卸载软件包,并删除相关的文件。 + +# Python __name__ + +首先需要了解 __name__ 是属于 python 中的内置类属性,就是它会天生就存在于一个 python 程序中,代表对应程序名称 + +```python +import requests +class requests(object): + def __init__(self,url): + self.url=url + self.result=self.getHTMLText(self.url) + def getHTMLText(url): + try: + r=requests.get(url,timeout=30) + r.raise_for_status() + r.encoding=r.apparent_encoding + return r.text + except: + return "This is a error." +print(__name__) +``` + +结果: + +```shell +__main__ +Process finished with exit code 0 +``` + +当这个 pcRequests.py 作为模块被调用时,则它的 __name__ 就是它自己的名字: + +```python +import pcRequestspcRequestsc=pcRequestsc.__name__ +``` + +结果: + +```shell +'pcRequests' +``` + +# UV + +## Python版本管理(安装和管理Python解释器) + +| 命令 | 说明 | +| ----------------------------- | -------------------------------------- | +| uv python list | 查看可用和已安装的Python 版本 | +| uv python install | 安装 Python 版本 | +| uv python find | 查找已安装的 Python信息 | +| uv python pin | 将当前项目固定使用特定 Python 版本 | +| uv python uninstall | 卸载 Python 版本 | +| uv python install --reinstall | 重新安装Python,适用于Python版本的更新 | + +## 项目管理(创建和开发带有 `pyproject.toml`的Python 项目) + +| **命令** | **说明** | +| ---------- | ------------------------------------------------------------ | +| uv init | 创建新 Python 项目并初始化该项目 | +| uv add | 为项目添加依赖,安装软件包并将依赖声明写入`pyproject.toml`文件 | +| uv remove | 从项目移除依赖,移除软件包并将依赖从文件`pyproject.toml`中移除 | +| uv sync | 同步项目依赖到环境 | +| uv lock | 为项目依赖创建锁文件 | +| uv run | 在项目环境中运行命令 | +| uv export | 将项目的锁文件导出为其他格式 | +| uv tree | 查看项目依赖树 | +| uv build | 构建项目为分发包 | +| uv publish | 发布项目到包索引 | + diff --git a/source/_posts/RabbitMQ.md b/source/_posts/RabbitMQ.md new file mode 100644 index 0000000..03e5808 --- /dev/null +++ b/source/_posts/RabbitMQ.md @@ -0,0 +1,5 @@ +--- +title: RabbitMQ +date: 2024-03-26 12:48:20 +tags: +--- diff --git a/source/_posts/React.md b/source/_posts/React.md new file mode 100644 index 0000000..2a3cc72 --- /dev/null +++ b/source/_posts/React.md @@ -0,0 +1,606 @@ +--- +title: React +date: 2025-07-23 15:56:46 +tags: +--- + +# Vue 转 React 指南 + +## JSX + +先介绍 React 唯一的一个语法糖:JSX。 + +理解 JSX 语法并不困难,简单记住一句话,遇到 `{}` 符号内部解析为 JS 代码,遇到成对的 `<>` 符号内部解析为 HTML 代码。 + +当你写下这个 React 组件时: + +```jsx +import React from 'react'; + +function MyComponent(props) { + return <div>{props.hello}</div> +} +``` + +最终会被自动工具翻译成: + +```jsx +import React from 'react'; + +function MyComponent(props) { + return React.createElement('div', null, props.hello); +} +``` + +React 就是通过这个小小语法糖,实现在 JS 里面写 HTML,可能有小伙伴会说 HTML 与 JS 分离不是更好吗?责职分明,混合只会更乱。但当你体验到代码自动提示,自动检查,以及调试时精确定位到一行代码的好处时,就清楚 React 和 Vue 的差距了。 + +## 文本插值 + +**vue种采用双括号** + +```vue +<span>Message: {{ msg }}</span> +``` + +**react采用单括号** + +```jsx +function MyComponent(props) { + let msg = 'XXX' + return <div>{ msg }</div> +} +``` + +## Attribute 绑定 + +**vue中 想要响应式地绑定一个 attribute,应该使用 `v-bind` 指令** + +```vue +<div v-bind:id="dynamicId"></div> +<div :id="dynamicId"></div> +``` + +**react中,使用单引号,或者使用单括号包裹表示动态绑定** + +```jsx +function App () { + let tmpID = '12' + return ( + <div className='App'> + <div id='12'>id</div> + <div id={tmpID}>id</div> + </div> + ); +} +``` + +动态绑定多值: + +```jsx +function App () { + let tmpObject = { + id: 13, + className: 'wrapper' + } + return ( + <div className='App'> + <div {...tmpObject}>id</div> + </div> + ); +} + +即: + +<div id="13" class="wrapper">id</div> +``` + +## 参数 Arguments + +**某些指令会需要一个“参数”,Vue在指令名后通过一个冒号隔开做标识。例如用 `v-bind` 指令** + +```vue +<a v-bind:href="url"> ... </a> + +<!-- 简写 --> +<a :href="url"> ... </a> +``` + +**React中则没有指令一说,而是采用如下方式:** + +```jsx +// href跳转 +function App () { + let tmpURL = 'https://www.XXXXXXXX' + return ( + <div className='App'> + <a href={tmpURL}></a> + </div> + ); +} +``` + +## 使用 JS 表达式 + +**React 遇到 `{}` 符号内部解析为 JS 代码** + +```jsx +function App () { + let tmpString = '--'; + return ( + <div className='App'> + <div >{1 + 1}</div> + <div >{'a' + 'b'}</div> + <div >{`1${tmpString}1`}</div> + </div> + ); +} +``` + +即: + +```html +<div>2</div> +<div>ab</div> +<div>1--1</div> +``` + +## 事件处理 + +**Vue中绑定事件处理:** + +```vue +<!-- `greet` 是上面定义过的方法名 --> +<button @click="greet">Greet</button> +``` + +**React可以通过在组件中声明 事件处理 函数来响应事件** + +React中点击事件使用小驼峰形式:onClick + +在标签上写函数: + +```jsx +function App () { + return ( + <div className='App'> + <div onClick={() => alert('点击出现弹框')}>按钮</div> + </div> + ); +} +``` + +提前声明函数: + +```jsx +function App () { + function myFun () { + alert('点击出现弹框') + } + return ( + <div className='App'> + <div onClick={myFun}>按钮</div> + </div> + ); +} +``` + +注意,`onClick={handleClick}` 的结尾没有小括号!不要 **调用** 事件处理函数:你只需 **传递给事件** 即可。当用户点击按钮时,React 会调用你的事件处理函数。 + +函数传参: + +```jsx +function App () { + function myFun (str) { + alert(str) + } + return ( + <div className='App'> + <div onClick={() => myFun('点击出现弹框')}>按钮</div> + </div> + ); +} +``` + +## 动态参数 + +**Vue在指令参数上也可以使用一个 JavaScript 表达式,需要包含在一对方括号内:** + +```vue +<a v-bind:[attributeName]="url"> ... </a> + +<!-- 简写 --> +<a :[attributeName]="url"> ... </a> +``` + +举例来说,如果你的组件实例有一个数据属性 `attributeName`,其值为 `"href"`,那么这个绑定就等价于 `v-bind:href`。 + +**React 也可以通过动态参数绑定** + +```jsx +function App () { + const obj = { + onClick: () => alert('点击出现弹框'), + // ...还可以写更多事件 + } + return ( + <div className='App'> + <div {...obj}>按钮</div> + </div> + ); +} +``` + +## 修饰符 Modifiers + +vue 修饰符是以点开头的特殊后缀 + +表明指令需要以一些特殊的方式被绑定。例如 `.prevent` 修饰符会告知 `v-on` 指令对触发的事件调用 `event.preventDefault()`: + +```vue +<form @submit.prevent="onSubmit">...</form> +``` + +React 则是依靠于JS基础 + +```jsx +function App () { + function onSubmit(e){ + e.preventDefault(); + e.stopPropagation(); + } + return ( + <div className='App'> + <form onSubmit={onSubmit}> + <button type='submit'></button> + </form> + </div> + ); +} +``` + +## 响应式 + +**为了实现视图更新,VUE中响应式是一个重要的概念** + +**而 React 中并没有响应式这个概念,要实现视图更新,需要在 React 引入 `useState`** + +通常,你会希望你的组件 “记住” 一些信息并展示出来。例如,也许你想计算一个按钮被点击的次数。要做到这一点,你需要在你的组件中添加 **state**。 + +首先,从 React 引入 `useState`: + +```jsx +import { useState } from 'react'; +``` + +现在你可以在你的组件中声明一个 **state 变量**: + +```jsx +function MyButton() { + const [count, setCount] = useState(0); + // ... +``` + +你将从 `useState` 中获得两样东西:当前的 state(`count`),以及用于更新它的函数(`setCount`)。你可以给它们起任何名字,但按照惯例,需要像这样 `[something, setSomething]` 为它们命名。 + +第一次显示按钮时,`count` 的值为 `0`,因为你把 `0` 传给了 `useState()`。当你想改变 state 时,调用 `setCount()` 并将新的值传递给它。点击该按钮计数器将递增: + +```jsx +function MyButton() { + const [count, setCount] = useState(0); + function handleClick() { + setCount(count + 1); + } + + return ( + <button onClick={handleClick}> + Clicked {count} times + </button> + ); +} +``` + +React 将再次调用你的组件函数。这次,`count` 会变成 `1`。接着,变成 `2`。以此类推。 + +如果你多次渲染同一个组件,每个组件都会拥有自己的 state。你可以尝试点击不同的按钮: + +## 计算属性 + +Vue中使用 computed 来实现计算属性(缓存计算的结果) + +**React 在组件的顶层调用 `useMemo` 来缓存每次重新渲染都需要计算的结果** + +```jsx +import { useState } from 'react'; +import { useMemo } from 'react'; + +function App () { + const [user] = useState({ firstname: 'a', lastname: 'b' }) + + const fullname = useMemo(() => { + return user.firstname + user.lastname; + }, [user.firstname, user.lastname]) + + return ( + <div className='App'> + {fullname} + </div> + ); +} +``` + +**useMemo(calculateValue, dependencies)** + +参数 + +- `calculateValue`:要缓存计算值的函数。它应该是一个没有任何参数的纯函数,并且可以返回任意类型。React 将会在首次渲染时调用该函数;在之后的渲染中,如果 `dependencies` 没有发生变化,React 将直接返回相同值。否则,将会再次调用 `calculateValue` 并返回最新结果,然后缓存该结果以便下次重复使用。 +- `dependencies`:所有在 `calculateValue` 函数中使用的响应式变量组成的数组。响应式变量包括 props、state 和所有你直接在组件中定义的变量和函数。如果你在代码检查工具中 [配置了 React](https://react.docschina.org/learn/editor-setup#linting),它将会确保每一个响应式数据都被正确地定义为依赖项。依赖项数组的长度必须是固定的并且必须写成 `[dep1, dep2, dep3]` 这种形式。React 使用 [`Object.is`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/is) 将每个依赖项与其之前的值进行比较。 + +## 绑定 HTML class + +数据绑定的一个常见需求场景是操纵元素的 CSS class 列表和内联样式。因为 `class` 和 `style` 都是 attribute + +Vue中可以给 `:class` (`v-bind:class` 的缩写) 传递一个对象来动态切换 class: + +```vue +<div :class="{ active: isActive }"></div> +``` + +上面的语法表示 `active` 是否存在取决于数据属性 `isActive` 的真假值。 + +React实现方式基于js语法,其实有多种实现方式,列举三元运算符方式如下: + +```jsx +function App () { + let showColor = false + return ( + // 现有box-show、box-hide两个class样式 + <div className={showColor ? 'box-show' : 'box-hide'}></div> + ); +} +``` + +## 语法糖转换 + +习惯 Vue 的同学都知道很多语法糖,比如 `v-if`、`v-for`、`v-bind`、`v-on` 等,相比 Vue,React 只有一个语法糖,那就是 jsx/tsx。`v-if` 这些功能在 React 上都是通过原生 javascript 实现的,慢慢你会发现,其实你学的不是 React,而是 Javascipt,React 赋予你通过 js 完整控制组件的能力,这部分明显比 Vue 的语法糖更加灵活,糖太多容易引来虫子(Bug) + +条件渲染 + +vue 中写法是这样: + +```vue +<div> + <h1 v-if="ishow">Vue is awesome!</h1> + <h1 v-else>else</h1> +</div> +``` + +在 React 函数组件中,只需使用 js 三目运算符语法即可完成条件渲染的功能。或者使用 && 逻辑,记住下面一句话就能过理解了: + +> 遇到 `{}` 符号内部解析为 JS 代码,遇到成对的 `<>` 符号内部解析为 HTML 代码 + +```jsx +function App () { + const ishow = false + return ( + <div> + {ishow ? <div>awesome</div> : <div>else</div>} + {ishow && <h1>React!</h1>} + </div> + ); +} +``` + +## 列表渲染 + +Vue中通过v-for进行列表渲染 + +**React 通过 js 的数组语法 map,将数据对象映射为 DOM 对象**。只需学会 js,无需记住各种指令,如果要做列表过滤,直接使用 `items.filter(...).map(...)` 链式调用即可,语法上更加灵活,如果为了提高渲染性能,使用 useMemo 进行优化即可,类似 Vue 的 computed。 + +```jsx +function App () { + const arr = [{ message: 'react' }, { message: 'JS' }] + return ( + <div> + {arr.map((items, index) => <li key={index}>{items.message}</li>)} + </div > + ); +} +``` + +## 侦听器 + +Vue中使用 watch监听数据变化,触发回调 + +React中可以使用 useEffect 实现 + +```jsx +function App () { + const [user, setUser] = useState({ + firstname: 'a', + lastname: 'b' + }) + useEffect(() => { + console.log("1111") + }, [user.firstname]) + return ( + <div> + <button onClick={() => setUser({ ...user, firstname: 'a2' })}></button> + </div > + ); +} +``` + + + +# 父子组件传递事件 + +## 子组件是模态框,确定按钮需要增加loading状态 + +### **子组件:增加 `confirmLoading` 状态并优化 `handleOk` 方法** + +```tsx +import React, { useState } from 'react'; +import { Modal, Checkbox, Input, message } from 'antd'; +import { useTranslation } from 'react-i18next' + +const { TextArea } = Input; + +interface FeedbackModalProps { + open: boolean; + onOk: (selectedOption: number | null, feedbackText: string) => Promise<void>; + onCancel: () => void; +} + +const FeedbackModal: React.FC<FeedbackModalProps> = ({ open, onOk, onCancel }) => { + const [selectedOption, setSelectedOption] = useState<number | null>(null); + const [feedbackText, setFeedbackText] = useState<string>(''); + const { t } = useTranslation() + const [confirmLoading, setConfirmLoading] = useState(false); + + const handleOk = async () => { + if (!feedbackText) { + message.warning('请填写反馈建议'); + return; + } + setConfirmLoading(true) + try { + await onOk(selectedOption, feedbackText); + } finally { + setConfirmLoading(false) + } + + }; + + return ( + <Modal + title="反馈" + open={open} + onOk={handleOk} + onCancel={onCancel} + okText={`${t('common.operation.confirm')}`} + cancelText={`${t('common.operation.cancel')}`} + confirmLoading={confirmLoading} + > + {/* <div> + <Checkbox + checked={selectedOption === 0} + onChange={() => setSelectedOption(0)} + > + 新增 + </Checkbox> + <Checkbox + checked={selectedOption === 1} + onChange={() => setSelectedOption(1)} + > + 修改 + </Checkbox> + </div> */} + <TextArea + rows={4} + placeholder="请输入您的反馈建议" + value={feedbackText} + onChange={(e) => setFeedbackText(e.target.value)} + /> + </Modal> + ); +}; + +export default FeedbackModal; + +``` + +### **父组件:确保 `handleFeedbackSubmit` 返回 Promise** + +```tsx + const handleFeedbackSubmit = useCallback( + async (selectedOption: number | null, feedbackText: string) => { + console.log(currentMessageId); + console.log(currentFeedback); + + try { + // 构造请求参数 + const requestBody = { + operationType: currentFeedback!.rating, // 0 或 1 + feedbackText, // 用户反馈建议 + conversationId: currentConversationId, // 会话 ID + messageId: currentMessageId, // 消息 ID + username: getCookieValue('username'), // 用户名 + }; + + // 调用 Java 接口 + const javaResponse = await fetch( + `/dev-api/api/conversation/feedback`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(requestBody), + } + ); + + // 调用原有的 updateFeedback 函数 + await updateFeedback( + { + url: `/messages/${currentMessageId}/feedbacks`, + body: { rating: currentFeedback!.rating }, + }, + // isInstalledApp, + // appId + ); + + // 显示成功通知 + notify({ type: "success", message: t("common.api.success") }); + + if (resolveFeedback) { + resolveFeedback(true); // 用户取消了反馈 + setResolveFeedback(null); + } + + // 关闭对话框 + setIsFeedbackModalVisible(false); + } catch (error) { + console.error("Error:", error); + notify({ type: "error", message: t("common.api.failed") }); + } finally { + setIsSubmittingNow(false); + } + }, + [ + currentMessageId, + currentFeedback, + currentConversationId, + isInstalledApp, + appId, + notify, + t, + ] + ); +``` + +父组件调用子组件部分 + +```tsx + <FeedbackModal + open={isFeedbackModalVisible} + onOk={handleFeedbackSubmit} + onCancel={() => { + if (resolveFeedback) { + resolveFeedback(false); // 用户取消了反馈 + setResolveFeedback(null); + } + setIsFeedbackModalVisible(false) + }} + /> +``` + +### **关键点说明** + +1. **子组件内部管理加载状态** + 通过 `useState` 创建 `isLoading` 状态,并在 `handleOk` 中控制其值。点击按钮时,`isLoading` 变为 `true`,请求结束后变为 `false`。 +2. **`okButtonProps` 绑定加载状态** + Ant Design 的 `Modal` 组件支持通过 `okButtonProps` 自定义按钮属性,这里将 `loading` 绑定到 `isLoading` 状态。 +3. **`onOk` 作为异步函数传递** + 父组件的 `handleFeedbackSubmit` 是 `async` 函数,返回 Promise。子组件通过 `await onOk()` 确保加载状态在请求结束后更新。 +4. **错误处理不影响加载状态** + 使用 `try...finally` 确保无论请求成功或失败,`isLoading` 都会被重置。 diff --git a/source/_posts/Redis.md b/source/_posts/Redis.md new file mode 100644 index 0000000..1ec9270 --- /dev/null +++ b/source/_posts/Redis.md @@ -0,0 +1,295 @@ +--- +title: Redis +date: 2022-12-06 22:18:31 +author: 文永达 +top_img: https://gcore.jsdelivr.net/gh/volantis-x/cdn-wallpaper/abstract/67239FBB-E15D-4F4F-8EE8-0F1C9F3C4E7C.jpeg +--- +--- + +# 安装Redis + +下载解压后 + +```shell +tar -zxvf redis-4.0.11.tar.gz +``` + +安装gcc + +```shell +yum install -y gcc +``` + +进入redis解压目录 + +使用make命令 + +```shell +make MALLOC=libc +make install PREFIX=/usr/redis +``` + +# Redis配置 + +复制解压后的安装程序文件夹里的redis.conf + +到redis安装文件夹 + +修改监听端口 + +```conf +bind 127.0.0.1 6379 +``` + +修改库的个数 + +```shell +# Set the number of databases. The default database is DB 0, you can select +# a different one on a per-connection basis using SELECT <dbid> where +# dbid is a number between 0 and 'databases'-1 +databases 16 +``` +修改端口号port + +```shell +# Accept connections on the specified port, default is 6379 (IANA #815344). +# If port 0 is specified Redis will not listen on a TCP socket. +port 6379 +``` + +设置密码 + +```conf +requirepass yourpassword +``` + +# Redis指令 + +Redis 命令用于在 redis 服务上执行操作。 + +要在 redis 服务上执行命令需要一个 redis 客户端。Redis 客户端在我们之前下载的的 redis 的安装包中。语法 + +Redis 客户端的基本语法为: + +``` +$ redis-cli +``` + +### 实例 + +以下实例讲解了如何启动 redis 客户端: + +启动 redis 服务器,打开终端并输入命令 **redis-cli**,该命令会连接本地的 redis 服务。 + +```shell +$ redis-cli +redis 127.0.0.1:6379> +redis 127.0.0.1:6379> PING + +PONG +``` + +在以上实例中我们连接到本地的 redis 服务并执行 **PING** 命令,该命令用于检测 redis 服务是否启动。在远程服务上执行命令 + +如果需要在远程 redis 服务上执行命令,同样我们使用的也是 **redis-cli** 命令。 + +### 语法 + +```shell +$ redis-cli -h host -p port -a password +``` + +### 实例 + +以下实例演示了如何连接到主机为 127.0.0.1,端口为 6379 ,密码为 mypass 的 redis 服务上。 + +```shell +$redis-cli -h 127.0.0.1 -p 6379 -a "mypass" +redis 127.0.0.1:6379> +redis 127.0.0.1:6379> PING + +PONG +``` + +## key操作指令 + +1. set: 设置key-value + +2. get: 获取指定的key对应的value + +3. keys *: 查看所有的key + +4. del: 删除一个key或多个key。自动忽略掉不存在的key + +5. exists: 判断一个key是否存在,如果存在返回1,不存在返回0 + +6. expire: 为某一个key设置过期时间单位秒seconds + +7. move: 将当前数据库中的key移动到其他库 + +8. pexpire: 为某一个key设置过期时间单位毫秒milliseconds + +9. (expireat)pexpireat: 以毫秒为单位设置过期时间戳 + +10. ttl: 查看对应key的过期时间。单位是秒。 + +11. pttl: 查看对应key的过期时间。单位是毫秒。 + +12. randomkey: 随机返回一个key + +13. rename: 重命名 + +14. type: 查看key对应的数据类型 + +## Redis数据类型 + +### string类型 + 1. mset: 一次设置多个key-value + 2. mget: 一次获取多个key-value + 3. getset: 获得原始的key的值,同时设置新值,如果不存在key则新建一个key + 4. strlen: 获得对应的key的存储的value的长度 + 5. append: 为对应的key的value追加内容 + 6. getrange: 字符串截取 + +### List类型 + +> 相当于Java中的List集合 +> +> 特点:有序(添加的先后顺序),可以重复 + + 1. lpush: 将某个值加入到一个list列表,将这个元素添加到列表的头部 + 2. lrange:获取某一个下标区间内的元素 + 3. rpush:将某个值加入到一个list列表,将这个元素添加到列表的尾部 + 4. lpushx:和lpush基本相同,必须要保证这个key是否存在 + 5. rpushx:和rpush基本相同,必须要保证这个key是否存在 + 6. lpop:返回和移除列表左边的第一个元素 + 7. rpop:返回和移除列表右边的第一个元素 + 8. llen:获取列表元素的个数 + 9. lset:设置某一个指定索引的值(索引有效),修改操作 + 10. lindex:获取某一个指定索引位置的元素 + 11. lrem:删除重复元素(少用) + 12. ltrim:保留列表中特定区间内的元素。列表的截取 + 13. linsert:在某一个元素之前或者之后插入元素 + +### Set类型 + +> 相当于Java中的set集合 +> +> 特点:无序,不可以重复,若重复,则会覆盖 + +1. sadd:为集合添加元素 +2. smembers:显示集合中的所有元素。无序(添加的先后顺序) +3. scard:返回集合中的元素的个数 +4. spop:随机返回一个元素并将元素在集合中删除 +5. smove:从一个集合中向另一个集合中移动元素(前提:必须是同一种类型) +6. srem:从集合中删除一个元素 +7. sismember:判断一个集合中是否有这个元素 +8. srandmember:随机返回一个元素 +9. sdiff:去掉第一个集合中和第二个集合中相同的元素 +10. sinter:求交集 +11. sunion:求并集 + + +### Zset类型 + +> 相当于Java中的TreeSet集合 +> +> 特点:不可以重复,但有序 + + 1. zadd:添加一个有序集合,添加元素时需要指定每一个元素的分数 + 2. zcard:返回集合中元素的个数 + 3. zrange:升序排列集合返回一个范围内的元素 + 4. zrevrange:降序排列 + 5. zrangebyscore:安装分数查找一个范围内的元素(类似于分页) + 6. zrank:返回排名(升序) + 7. zrevrank:返回排名(降序) + 8. zscore:查看一个元素的分数 + 9. zrem:移除某一个元素 + 10. zincrby:给某一个元素加分 + +### Hash类型 + +> 相当于Java中的Map集合 +> +> 特点:key不可重复,value可重复。无序 + +1. hset:设置一个key/value对 +2. hget:获取一个key对应的value +3. hgetall:获得所有的key/value对 +4. hdel: 删除某一个key/value对 +5. hexists:判断一个key是否存在 +6. hkeys:获得所有的key +7. hvals:获得所有的value +8. hmset:设置多个key/value +9. hmget:获取多个key/value +10. hsetnx:设置一个不存在的key的值 +11. hincrby:为value进行加法计算 +12. hincrbyfloat:为value进行加分计算,小数 + +# Redis主从架构 + +在redis主从架构中,Master节点负责处理写请求,Slave节点只处理读请求。对于写请求少,读请求多的场景,例如电商详情页,通过这种读写分离的操作可以大幅提高并发量,通过增加redis从节点的数量可以使得redis的QPS达到10W+。 + +在根目录下创建三个文件夹 master slave1 slave2 + +```shell +mkdir master slave1 slave2 +``` + +将redis压缩包解压后文件夹内的redis.conf分别复制到这三个文件夹中 + +修改 master redis.conf内容 + +```shell +vim redis.conf +:/bind #查找bind +bind 0.0.0.0 #修改bind为0.0.0.0 让其可被外界访问 +``` + +修改 slave1和slave2 redis.conf内容 + +```shell +vim redis.conf +:/bind #查找bind +bind 0.0.0.0 #修改bind为0.0.0.0 让其可被外界访问 +:/slave #查找slave +slaveof <masterip> <masterport> #将这行注释取消 +# 修改为 对应主机redis的ip 对应主机redis的port +slaveof 192.168.56.101 6379 +``` + +此时redis主从机构配置好了,主机负责读和写,主要负责写,而从机负责读,但不能写。 + +此时问题来了,如果主机挂了,那么数据就没法写到redis缓存中,从机只负责读,不能写,但此时仍可读数据 + +此时就需要用到Redis哨兵机制 + +# Redis哨兵机制 + +哨兵机制就是起一台机器用作哨兵,这个哨兵负责监听主机,如果主机挂了,那么它会让从机接替主机位,以此类推,需要用到选举 + +在根目录下新建sentinel文件夹 + +```shell +mkdir sentinel +vim sentinel.conf +# 第一行输入如下 哨兵名 主机ip 主机port 选举数 +sentinel monitor mymaster 192.168.56.101 6379 1 +``` + +将sentinel.conf 复制到redis安装目录 bin下 + +```shell +cp /root/sentinel/sentinel.conf /usr/redis/bin +``` + +启动哨兵 + +```shell +./redis-sentinel sentinel.conf +``` + +# Redis缓存穿透 + +> 缓存穿透指的查询缓存和数据库中都不存在的数据,这样每次请求直接打到数据库,就好像缓存不存在一样。 + diff --git a/source/_posts/Rust.md b/source/_posts/Rust.md new file mode 100644 index 0000000..81eb04b --- /dev/null +++ b/source/_posts/Rust.md @@ -0,0 +1,1336 @@ +--- +title: Rust +date: 2023-09-15 09:25:54 +tags: +--- + +# Rust 教程 + +## 第一个 Rust 程序 + +Rust语言代码文件后缀名为`.rs`,如helloworld.rs。 + +```rust +fn main() { + println!("Hello World!"); +} +``` + +使用`rustc`命令编译helloworld.rs文件: + +```shell +rustc helloworld.rs # 编译 helloworld.rs 文件 +``` + +编译后会生成helloworld可执行文件: + +```shell +./helloworld # 执行 helloworld +Hello World! +``` + +# Rust 环境搭建 + +## 安装 Rust 编译工具 + +Rust 编译工具从链接 [安装 Rust - Rust 程序设计语言 (rust-lang.org)](https://www.rust-lang.org/zh-CN/tools/install) 中下载的Rustup安装。下载好的Rustup在Windows 上是一个可执行程序 rustup-init.exe。(在其他平台上应该是`rustup-init.sh`)。 + +现在执行 rustup-init 文件: + +![image-20230915102448742](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20230915102448742.png) + +![image-20230915135358199](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20230915135358199.png) + +上图显示的是一个命令行安装向导。 + +**如果你已经安装MSVC(推荐),那么安装过程会非常的简单,输入 1 并回车,直接进入第二步。** + +如果你安装的是MinGW,那么你需要输入 2(自定义安装),然后系统会询问你 Default host triple?,请将上图中 **default host triple**的"msvc"改为"gnu"再输入安装程序: + +![img](https://www.runoob.com/wp-content/uploads/2020/04/rust-env2.png) + +其他属性都默认。 + +设置完所有选项,会回到安装向导界面(第一张图),这时我们输入 1 并回车即可。 + +![img](https://www.runoob.com/wp-content/uploads/2020/04/rust-env3.png) + +进行到这一步就完成了Rust的安装,可以通过以下命令测试: + +```shell +rustc -V # 注意大写的 V +``` + +![img](https://www.runoob.com/wp-content/uploads/2020/04/rust-env4.png) + +如果以上两个命令能够输出你安装的版本号,就是安装成功了。 + +## 搭建 Visual Studio Code 开发环境 + +安装`rust-analyzer`和`Native Debug`两个扩展。 + +![img](https://www.runoob.com/wp-content/uploads/2020/04/49033261-B1B8-4D70-8090-53DC45A8727E.jpeg) + +![img](https://www.runoob.com/wp-content/uploads/2020/04/rust-env8.png) + +重新启动 VsCode,Rust 的开发环境就搭建好了。 + +现在新建一个文件夹,如 RustLearn。 + +在VsCode中打开新建的文件夹。 + +打开文件夹后,新建终端。 + +输入以下命令: + +```shell +cargo new greeting +``` + +当前文件夹下会构建一个名叫 greeting 的 Rust 的工程目录。 + +在终端里输入以下三个命令: + +```shell +cd ./greeting +cargo build +cargo run +``` + +系统在创建工程时会生成一个Hello World源程序main.rs,这时会被编译运行: + +# Rust 标准库中文版 + +该仓库包含 `rust-src` 组件的所有源代码文件,并对其所有的源代码进行翻译,主要包括对 Rust 核心库的翻译,Rust 标准库的翻译,以及其他一些资源。该仓库使用 [`Cmtor`](https://gitee.com/dirname/rust-library-chinese/tree/main#) (我写的效率工具) 程序并借助 `JSON` 文件来完成翻译的所有工作,当 Rust 更新时,将尽可能为其生成中文翻译。 + +## 下载翻译好的 Rust 文档 + +每次在构建新的中文文档时,会修复之前构建结果中存在的问题,为了尽可能的保证翻译的准确性,本仓库只提供最新版本的构建。最新的构建结果会放在 [`dist`](https://gitee.com/dirname/rust-library-chinese/blob/main/dist) 目录下,您可以手动跳转到该文件夹,下载最新的构建结果 + +## 使用 Rust 中文文档 + +> - 在使用中文文档时,请注意版本号,中文文档版本和 Rust 版本号必须要保持一致。 +> - 必须使用 `stable` 版本,不要使用 `beta` 和 `nightly` 版本。 +> - 在翻译后的源代码中,一些文档的底部会存在一定量的内容为空的注释行,其实这是有意为之,请不要擅自修改和删除。如果您删除了它,就会导致 `source-map` 失效,当 `source-map` 失效后,在调试源代码时就会出现执行位置和源代码位置不一致的严重问题。 + +请确保 Rust 已经安装好,并且可以正常工作。在 Rust 安装成功后,您还应该通过 `rustup component add rust-src` 命令来安装 `rust-src` 组件。当安装 `rust-src` 组件之后,请按照以下步骤进行操作: + +1. 在终端执行: `rustup default stable` 来切换到 `stable` 版本,并确保 `stable` 的版本与中文版文档所对应的版本一致 +2. 在终端执行 `rustup show`,然后在输出中找到 `rustup home` 所对应的路径,然后将其在资源管理器中打开 +3. 打开 `toolchains` 的文件夹,在该文件夹下,找到您当前所使用的 Rust 工具链并将其打开,例如,在 `Windows` 平台上对应的是 `stable-x86_64-pc-windows-msvc` 文件夹 +4. 然后打开 `lib/rustlib/src/rust` 目录,这个目录下的文件夹就是 Rust 标准库源代码所在的位置 +5. 将 `lib/rustlib/src/rust/library` 文件夹下的所有内容保存一份副本,然后删除 +6. 下载本仓库对应的中文文档源文件,`dist`目录下`zip`压缩包将其解压缩并将其下的 `library` 并放置到 `lib/rustlib/src/rust` 文件夹下 +7. 请确保您已经在 IDE 中安装 Rust 相关插件,例如,`vscode` 需要安装:[rust-analyzer](https://gitee.com/link?target=https%3A%2F%2Fmarketplace.visualstudio.com%2Fitems%3FitemName%3Dmatklad.rust-analyzer) +8. 重新启动 `IDE` 工具,中文文档的智能提示开始工作 +9. 愉快的编码! + +# Cargo 教程 + +## Cargo 是什么 + +Cargo 是 Rust 的构建系统和包管理器。 + +Rust 开发者 常用 Cargo 来管理 Rust 工程和获取工程所依赖的库。在上个教程中我们曾使用 cargo new greeting 命令创建一个名为 greeting 的工程,Cargo 新建了一个名为 greeting 的文件夹并在里面部署了一个 Rust 工程最典型的文件结构。这个 greeting 文件夹就是工程本身。 + +## Cargo 功能 + +Cargo 除了创建工程以外还具备构建(build)工程、运行(run)工程等一系列功能,构建和运行分别对应以下命令: + +```shell +cargo build +cargo run +``` + +Cargo 还具有获取包、打包、高级构建等功能,详细使用方法参见 Cargo 命令。 + +```shell +cargo clippy # 类似ESLint,lint工具检查代码可以优化的地方 +cargo fmt # 类似go fmt,代码格式化 +cargo tree # 查看第三方库的版本和依赖关系 +cargo bench # 运行benchmark(基准测试,性能测试) +cargo udeps # (第三方)检查项目中未使用的依赖 +# 另外cargo build/run --release 使用release编译会比默认的debug编译性能提升10倍以上,但是 release 缺点是编译速度较慢,而且不会显示 panic backtrace 的具体行号 +``` + +## 在 VsCode 中配置 Rust 工程 + +Cargo 是一个不错的构建工具,如果使VsCode 与它相配合那么 VsCode 将会是一个十分便捷的开发环境。 + +在上一章中我们建立了 greeting 工程,现在我们用 VsCode 打开 greeting 文件夹**(注意不是 RustLeanrning)**。 + +打开 greeting 之后,在里面新建一个新的文件夹`.vscode` + +# Rust 输出到命令行 + +在正式学习 Rust 语言以前,我们需要先学会怎样输出一段文字到命令行,这几乎是学习每一门语言之前必备的技能,因为输出到命令行几乎是语言学习阶段程序表达结果的唯一方式。 + +在之前的 Hello, World 程序中大概已经告诉了大家输出字符串的方式,但并不全面,大家可能很疑惑为什么 println!( "Hello World") 中的 println 后面还有一个 `!` 符号,难道 Rust 函数之后都要加一个感叹号?显然并不是这样。println 不是一个函数,而是一个宏规则。这里不需要更深刻的挖掘宏规则是什么,后面的章节中会专门介绍,并不影响接下来的一段学习。 + +Rust 输出文字的方式主要有两种:`println!()` 和 `print!()`。这两个"函数"都是向命令行输出字符串的方法,区别仅在于前者会在输出的最后附加输出一个换行符。当用这两个"函数"输出信息的时候,第一个参数是格式字符串,后面是一串可变参数,对应着格式字符串中的"占位符",这一点与 C 语言中的 printf 函数很相似。但是,Rust 中格式字符串中的占位符不是 **"% + 字母"** 的形式,而是一对 **{}**。 + +`printlna.rs`文件 + +```rust +fn main() { + let a = 12; + println!("a is {}", a); +} +``` + +使用`rustc`命令编译`printlna.rs`文件: + +```rust +rustc printlna.rs # 编译 printlna.rs 文件 +``` + +编译后会生成 `printlna`可执行文件: + +```shell +./printlna # 执行 printlna +``` + +以上程序的输出结果是: + +```shell +a is 12 +``` + +如果我想把 a 输出两遍,那岂不是要写成: + +```rust +println!("a is {}, a again is {}", a, a); +``` + +其实有更好的写法: + +```rust +println!("a is {0}, a again is {0}", a); +``` + +在`{}`之间可以放一个数字,它将把之后的可变参数当做一个数组来访问,下标从0开始。 +{% raw %} +如果要输出 **{** 或 **}** 怎么办呢?格式字符串通过 **{{** 和 **}}** 分别转义代表 { 和 } 。但是其他常用转义字符与 C 语言里的转义字符一样,都是反斜杠开头的形式。 +{% endraw %} +```rust +fn main() { + println!("{{}}"); +} +``` + +以上程序的输出结果是: + +```shell +{} +``` + +# Rust 格式化代码 + +## rustfmt + +只会格式化单个Rust文件 + +```shell +rustfmt main.rs +``` + +## cargo fmt + +会格式化整个Rust项目 + +```shell +cargo fmt +``` + +# Rust 构建 + +基于cargo 创建的整个项目进行构建 + +```shell +cargo build +# 简写 +cargo b +``` + +默认按照Debug模式进行构建 + +若进行Release模式构建 + +```shell +cargo build --release +cargo b --release +``` + +# Rust 运行 + +默认先按照Debug模式进行构建,然后再执行可执行文件 + +```shell +cargo run +# 简写 +cargo r +``` + +想只输出实际内容 + +```shell +cargo run --quiet +cargo r --quiet +``` + + + +# Rust 基础语法 + +变量,基本类型,函数,注释和控制流,这些几乎是每种编程语言都具有的编程概念。 + +这些基础概念将存在于每个 Rust 程序中,及早学习它们将使你以最快的速度学习 Rust 的使用。 + +## 变量 + +首先必须说明,Rust 是强类型语言,但具有自动判断变量类型的能力。这很容易让人与弱类型语言产生混淆。 + +如果要声明变量,需要使用 `let` 关键字。例如: + +```rust +let a = 123; +``` + +只学习过 JavaScript 的开发者对这句话很敏感,只学习过 C 语言的开发者对这句话很不理解。 + +在这句声明语句之后,以下三行代码都是被禁止的: + +```rust +a = "abc"; +a = 4.56; +a = 456; +``` + +第一行的错误在于当声明 a 是 123 以后,a 就被确定为整型数字,不能把字符串类型的值赋给它。 + +第二行的错误在于自动转换数字精度有损失,Rust 语言不允许精度有损失的自动数据类型转换。 + +第三行的错误在于 a 不是个可变变量。 + +前两种错误很容易理解,但第三个是什么意思?难道 a 不是个变量吗? + +这就牵扯到了 Rust 语言为了高并发安全而做的设计:在语言层面尽量少的让变量的值可以改变。所以 a 的值不可变。但这不意味着 a 不是"变量"(英文中的 variable),官方文档称 a 这种变量为"不可变变量"。 + +如果我们编写的程序的一部分在假设值永远不会改变的情况下运行,而我们代码的另一部分在改变该值,那么代码的第一部分可能就不会按照设计的意图去运转。由于这种原因造成的错误很难在事后找到。这是 Rust 语言设计这种机制的原因。 + +当然,使变量变得"可变"(mutable)只需一个 `mut`关键字。 + +```rust +let mut a = 123; +a = 456; +``` + +这个程序是正确的。 + +## 常量与不可变变量的区别 + +既然不可变变量是不可变的,那不就是常量吗?为什么叫变量? + +变量和常量还是有区别的。在 Rust 中,以下程序是合法的: + +```rust +let a = 123; // 可以编译,但可能有警告,因为该变量没有被使用 +let a = 456; +``` + +但是如果 a 是常量就不合法: + +```rust +const a: i32 = 123; +let a = 456; +``` + +变量的值可以"重新绑定",但在"重新绑定"以前不能私自被改变,这样可以确保在每一次"绑定"之后的区域里编辑器可以充分的推理程序逻辑。虽然 Rust 有自动判断类型的功能,但有些情况下声明类型更加方便: + +```rust +let a: u64 = 123; +``` + +这里声明了 a 为无符号 64 位整型变量,如果没有声明类型,a 将自动被判断为有符号 32 位整型变量,这对于 a 的取值范围有很大的影响。 + +## 重影 (Shadowing) + +重影的概念与其他面向对象语言里的"重写"(Override)或"重载"(Overload)是不一样的。重影就是刚才讲述的所谓"重新绑定",之所以加引号就是为了在没有介绍这个概念的时候代替一下概念。 + +重影就是指变量的名称可以被重新使用的机制: + +```rust +fn main() { + let x = 5; + let x = x + 1; + let x = x * 2; + println!("The value of x is: {}", x); +} +``` + +这段程序的运行结果: + +```shell +The value of x is: 12 +``` + +重影与可变变量的赋值不是一个概念,重影是指用同一个名字重新代表另一个变量实体,其类型、可变属性和值都可以变化。但可变变量赋值仅能发生值的变化。 + +```rust +let mut s = "123"; +s = s.len(); +``` + +这段程序会出错:不能给字符串变量赋整型值。 + +# Rust 数据类型 + +Rust 语言中的基础数据类型有以下几种。 + +## 整数型(Integer) + +整数型简称整形,按照比特位长度和有无符号分为以下种类: + +| 位长度 | 有符号 | 无符号 | +| ------- | ------ | ------ | +| 8-bit | i8 | u8 | +| 16-bit | i16 | u16 | +| 32-bit | i32 | u32 | +| 64-bit | i64 | u64 | +| 128-bit | i128 | u128 | +| arch | isize | usize | + +iszie 和 usize 两种整数类型是用来衡量数据大小的,它们的位长度取决于所运行的目标平台,如果是 32 位架构的处理器将使用 32 位位长度整型。 + +整数的表述方法有以下几种: + +| 进制 | 例 | +| -------------------- | ----------- | +| 十进制 | 98_222 | +| 十六进制 | 0xff | +| 八进制 | 0o77 | +| 二进制 | 0b1111_0000 | +| 字节(只能表示 u8 型) | b'A' | + +很显然,有的整数中间存在一个下划线,这种设计可以让人们在输入一个很大的数字时更容易判断数字的值大概是多少。 + +## 浮点整型(Floating-Point) + +Rust 与其他语言一样支持 32 位浮点数(f32)和 64 位浮点数(f64)。默认情况下, 64.0 将表示 64 位浮点数,因为现代计算机处理器对两种浮点数计算的速度几乎相同,但 64 位浮点数精度更高。 + +```rust +fn main() { + let x = 2.0 // f64 + let y: f32 = 3.0; // f32 +} +``` + +## 数学运算 + +用一段程序反映数学运算: + +```rust +fn main() { + let sum = 5 + 10; // 加 + let difference = 95.5 - 4.3; // 减 + let product = 4 * 30; // 乘 + let quotient = 56.7 ? 32.2; // 除 + let remainder = 43 % 5; // 求余 +} +``` + +许多运算符号之后加上 = 号是自运算的意思,例如: + +`sum + 1` 等同于 `sum = sum + 1`。 + +**注意:**Rust 不支持 **++** 和 **--**,因为这两个运算符出现在变量的前后会影响代码可读性,减弱了开发者对变量改变的意识能力。 + +## 布尔型 + +布尔型用 bool 表示,值只能为 true 和 false。 + +## 字符型 + +字符型用 char 表示。 + +Rust 的 char 类型大小为 4 个字节,代表 Unicode 标量值,这意味着它可以支持中文,日文和韩文字符等非英文字符甚至表情符号和零宽度空格在 Rust 中都是有效的 char 值。 + +Unicode 值的范围从 U+0000 到 U+D7FF 和 U+E000 到 U+10FFFF(包括两端)。但是,"字符"这个概念并不存在与 Unicode 中,因此您对"字符"是什么的直觉可能与Rust中的字符概念不匹配。所以一般推荐使用字符串储存 UTF-8 文字(非英文字符尽可能地出现在字符串中)。 + +**注意:**由于中文文字编码有两种(GBK 和 UTF-8),所以编程中使用中文字符串有可能导致乱码的出现,这时因为源程序与命令行的文字编码不一致,所以在 Rust 中字符串和字符都必须使用 UTF-8 编码,否则编译器会报错。 + +## 复合类型 + +元组是一对`( )`包括的一组数据,可以包含不同种类的数据: + +```rust +let tup: (i32, f64, u8) = (500, 6.4, 1); +// tup.0 等于 500 +// tup.1 等于 6.4 +// tup.2 等于 1 +let (x, y, z) = tup; +// y 等于 6.4 +``` + +数组用一对`[ ]`包括的同类型数据。 + +```rust +let a = [1, 2, 3, 4, 5]; +// a 是一个长度为 5 的整型数组 + +let b = ["January", "February", "March"]; +// b 是一个长度为 3 的字符串数组 + +let c = [i32; 5] = [1, 2, 3, 4, 5]; +// c 是一个长度为 5 的 i32 数组 + +let d = [3; 5]; +// 等同于 let d = [3, 3, 3, 3, 3]; + +let first = a[0]; +let second = a[1]; +// 数组访问 + +a[0] = 123; // 错误:数组 a 不可变 +let mut a = [1, 2, 3]; +a[0] = 4; // 正确 +``` + +# Rust 函数 + +函数在 Rust 语言中是普遍存在的。 + +通过之前的章节已经可以了解到 Rust 函数的基本形式: + +```rust +fn <函数名> ( <参数> ) { <参数体> } +``` + +其中 Rust 函数名称的命名风格是小写字母以下划线分割: + +```rust +fn main() { + println!("Hello, world!"); + another_function(); +} + +fn another_function() { + println!("Hello, another!"); +} +``` + +运行结果: + +```shell +Hello, world! +Hello, another! +``` + +注意,我们在源代码中的 main 函数之后定义了 another_function。Rust 不在乎您在何处定义函数,只需在某个地方定义它们即可。 + +## 函数参数 + +```rust +fn <函数名> ( <参数> ) <函数体> +``` + +Rust 中定义函数如果需要具备参数必须声明参数名称和类型: + +```rust +fn main() { + another_function(5, 6); +} + +fn another_function(x: i32, y: i32) { + println!("x 的值为 :{}", x); + println!("y 的值为 :{}", y); +} +``` + +运行结果: + +```shell +x 的值为 :5 +y 的值为 :6 +``` + +## 函数体的语句和表达式 + +Rust 函数体由一系列可以以表达式(Expression)结尾的语句(Statement)组成。到目前为止,我们仅见到了没有以表达式结尾的函数,但已经将表达式用作语句的一部分。 + +语句是执行某些操作且没有返回值的步骤。例如: + +```rust +let a = 6; +``` + +这个步骤没有返回值,所以以下语句不正确: + +```rust +let a = (let b = 2); +``` + +表达式有计算步骤且有返回值。以下是表达式(假设出现的标识符已经被定义): + +```rust +a = 7 +b + 2 +c * (a + b) +``` + +Rust 中可以在一个用`{}`包括的块里编写一个较为复杂的表达式: + +```rust +fn main() { + let x = 5; + + let y = { + let x = 3; + x + 1 + }; +} +``` + +# Rust 条件语句 + +Rust 中条件语句格式是这样的: + +```rust +fn main() { + let number = 3; + if number < 5 { + println!("条件为 true"); + } else { + println!("条件为 false"); + } +} +``` + +上述程序中,条件表达式 number < 5 不需要用小括号包括(注意,不需要不是不允许);但是 Rust 中的 if 不存在单语句不用加 {} 的规则,不允许使用一个语句代替一个块。尽管如此,Rust还是支持传统 else-if 语法的: + +```rust +fn main() { + let a = 12; + let b; + if a > 0 { + b = 1; + } + else if a < 0 { + b = -1; + } + else { + b = 0; + } + println!("b is {}", b); +} +``` + +运行结果: + +```rust +b is 1 +``` + +Rust 中的条件表达式必须是 bool 类型,例如下面的程序是错误的: + +```rust +fn main() { + let number = 3; + if number { *// 报错,expected `bool`, found integerrustc(E0308)* + println!("Yes"); + } +} +``` + +虽然 C/C++ 语言中的条件表达式用整数表示,非 0 即真,但这个规则在很多注重代码安全性的语言中是被禁止的。 + +结合之前章学习的函数体表达式我们加以联想: + +```rust +if <condition> { block 1 } else { block 2 } +``` + +在 Rust 中我们可以使用 if-else 结构实现类似于三元条件运算表达式 **(A ? B : C)** 的效果: + +```rust +fn main() { + let a = 3; + let number = if a > 0 { 1 } else { -1 }; + println!("number 为 {}", number); +} +``` + +运行结果: + +``` +number 为 1 +``` + +if 语句块的返回值也可以给 number 进行赋值 + +用 if 来赋值时,要保证每个分支返回的类型一样(这种说法也不完全准确),此处返回的 `5` 和 `6` 就是同一个类型,如果返回类型不一致就会报错 + +```rust +fn main() { + let condition = true; + let number = if condition { + 5 + } else { + 6 + }; + + println!("The value of number is: {}", number); +} +``` + +# Rust 循环 + +Rust 的循环结果设计也十分成熟。 + +## while 循环 + +while 循环是最典型的条件语句循环: + +```rust +fn main() { + let mut number = 1; + while number != 4 { + println!("{}", number); + number += 1; + } + println!("EXIT"); +} +``` + +运行结果: + +``` +1 +2 +3 +EXIT +``` + +在 C 语言中 for 循环使用三元语句控制循环,但是 Rust 中没有这种用法,需要用 while 循环来代替: + +C 语言 + +```c +int i; +for (i = 0; i < 10; i++) { + *// 循环体* +} +``` + +Rust + +```rust +let mut i = 0; +while i < 10 { + *// 循环体* + i += 1; +} +``` + +## for 循环 + +for 循环是最常用的循环结构,常用来遍历一个线性数据结构(比如数组)。for 循环遍历数组: + +```rust +fn main() { + let a = [10, 20, 30, 40, 50]; + for i in a.iter() { + println!("值为 : {}", i); + } +} +``` + +运行结果: + +``` +值为 : 10 +值为 : 20 +值为 : 30 +值为 : 40 +值为 : 50 +``` + +这个程序中的 for 循环完成了对数组 a 的遍历。a.iter() 代表 a 的迭代器(iterator),在学习有关于对象的章节以前不做赘述。 + +当然,for 循环其实是可以通过下标来访问数组的: + +```rust +fn main() { +let a = [10, 20, 30, 40, 50]; + for i in 0..5 { + println!("a[{}] = {}", i, a[i]); + } +} +``` + +运行结果: + +``` +a[0] = 10 +a[1] = 20 +a[2] = 30 +a[3] = 40 +a[4] = 50 +``` + +## loop 循环 + +某个循环无法在开头和结尾判断是否继续进行循环,必须在循环体中间某处控制循环的进行。如果遇到这种情况,我们经常会在一个 while (true) 循环体里实现中途退出循环的操作。 + +Rust 语言有原生的无限循环结构 —— loop: + +```rust +fn main() { + let s = ['R', 'U', 'N', 'O', 'O', 'B']; + let mut i = 0; + loop { + let ch = s[i]; + if ch == 'O' { + break; + } + println!("\'{}\'", ch); + i += 1; + } +} +``` + +运行结果: + +``` +'R' +'U' +'N' +``` + +loop 循环可以通过 break 关键字类似于 return 一样使整个循环退出并给予外部一个返回值。这是一个十分巧妙的设计,因为 loop 这样的循环常被用来当作查找工具使用,如果找到了某个东西当然要将这个结果交出去: + +```rust +fn main() { + let s = ['R', 'U', 'N', 'O', 'O', 'B']; + let mut i = 0; + let location = loop { + let ch = s[i]; + if ch == 'O' { + break i; + } + i += 1; + }; + println!(" \'O\' 的索引为 {}", location); +} +``` + +运行结果: + +``` + 'O' 的索引为 3 +``` + +# Rust 闭包 + +Rust 中的闭包是一种匿名函数,它们可以捕获并存储其环境中的变量。 + +闭包允许在其定义的作用域之外访问变量,并且可以在需要时将其移动或借用给闭包。 + +闭包在 Rust 中被广泛应用于函数编程、并发编程、和事件驱动编程等领域。 + +闭包在 Rust 中非常有用,因为它们提供了一种简洁的方式来编写和使用函数。 + +闭包在 Rust 中非常灵活,可以存储在变量中、作为参数传递,甚至作为返回值。 + +闭包通常用于需要短小的自定义逻辑的场景,例如迭代器、回调函数等。 + +## 闭包与函数的区别 + +| 特性 | 闭包 | 函数 | +| :------------- | :------------------------- | :--------------- | +| **匿名性** | 是匿名的,可存储为变量 | 有固定名称 | +| **环境捕获** | 可以捕获外部变量 | 不能捕获外部变量 | +| **定义方式** | ` | 参数 | +| **类型推导** | 参数和返回值类型可以推导 | 必须显式指定 | +| **存储与传递** | 可以作为变量、参数、返回值 | 同样支持 | + +以下是 Rust 闭包的一些关键特性和用法: + +## 闭包的声明 + +闭包的语法声明: + +```rust +let closure_name = |参数列表| 表达式或语句块; +``` + +参数可以有类型注解,也可以省略,Rust 编译器会根据上下文推断它们。 + +```rust +let add_one = |x: i32| x + 1; +``` + +**闭包的参数和返回值:** 闭包可以有零个或多个参数,并且可以返回一个值。 + +```rust +let calculate = |a, b, c| a * b + c; +``` + +**闭包的调用:**闭包可以像函数一样被调用。 + +```rust +let result = calculate(1, 2, 3); +``` + +## 匿名函数 + +闭包在 Rust 中类似于匿名函数,可以在代码中以 **{}** 语法块的形式定义,使用 **||** 符号来表示参数列表,实例如下: + +```rust +let add = |a, b| a + b; +println!("{}", add(2, 3)); // 输出: 5 +``` + +在这个示例中,add 是一个闭包,接受两个参数 a 和 b,返回它们的和。 + +## 捕获外部变量 + +闭包可以捕获周围环境中的变量,这意味着它可以访问定义闭包时所在作用域中的变量。例如: + +```rust +let x = 5; +let square = |num| num * x; +println!("{}", square(3)); // 输出: 15 +``` + +以上代码中,闭包 square 捕获了外部变量 x,并在闭包体中使用了它。 + +闭包可以通过三种方式捕获外部变量: + +- **按引用捕获**(默认行为,类似 `&T`) +- **按值捕获**(类似 `T`) +- **可变借用捕获**(类似 `&mut T`) + +```rust +fn main() { + let mut num = 5; + + // 按引用捕获 + let print_num = || println!("num = {}", num); + print_num(); // 输出: num = 5 + + // 按值捕获 + let take_num = move || println!("num taken = {}", num); + take_num(); // 输出: num taken = 5 + // println!("{}", num); // 若取消注释,将报错,num 所有权被转移 + + // 可变借用捕获 + let mut change_num = || num += 1; + change_num(); + println!("num after closure = {}", num); // 输出: num after closure = 6 +} +``` + +**说明:** + +- 闭包默认按引用捕获外部变量。 +- 使用 `move` 关键字可以强制按值捕获,将外部变量的所有权转移到闭包内。 +- 如果闭包需要修改外部变量,需显式声明为 `mut` 闭包。 + +## 移动与借用 + +闭包可以通过 **move** 关键字获取外部变量的所有权,或者通过借用的方式获取外部变量的引用。例如: + +**借用变量:**默认情况下,闭包会借用它捕获的环境中的变量,这意味着闭包可以使用这些变量,但不能改变它们的所有权。这种情况下,闭包和外部作用域都可以使用这些变量。例如: + +```rust +let x = 10; +let add_x = |y| x + y; +println!("{}", add_x(5)); // 输出 15 +println!("{}", x); // 仍然可以使用 x +``` + +**获取所有权:**通过在闭包前添加 move 关键字,闭包会获取它捕获的环境变量的所有权。这意味着这些变量的所有权会从外部作用域转移到闭包内部,外部作用域将无法再使用这些变量。例如: + +```rust +let s = String::from("hello"); +let print_s = move || println!("{}", s); +print_s(); // 输出 "hello" +// println!("{}", s); // 这行代码将会报错,因为 s 的所有权已经被转移给了闭包 +``` + +通过这两种方式,Rust 提供了灵活的机制来处理闭包与外部变量之间的关系,使得在编写并发、安全的代码时更加方便。 + +## 闭包的特性 + +### 闭包可以作为函数参数 + +闭包经常作为参数传递给函数,例如迭代器的 .map()、.filter() 方法: + +```rust +fn apply_to_value<F>(val: i32, f: F) -> i32 +where + F: Fn(i32) -> i32, +{ + f(val) +} + +fn main() { + let double = |x| x * 2; + let result = apply_to_value(5, double); + println!("Result: {}", result); // 输出: Result: 10 +} +``` + +这里的 Fn 是闭包的一个特性(trait),用于表示闭包可以被调用。 + +### 闭包可以作为返回值 + +闭包还可以作为函数的返回值。由于闭包是匿名的,我们需要使用 impl Trait 或 Box 来描述其类型。 + +使用 impl Fn 返回闭包 + +```rust +fn make_adder(x: i32) -> impl Fn(i32) -> i32 { + move |y| x + y +} + +fn main() { + let add_five = make_adder(5); + println!("5 + 3 = {}", add_five(3)); // 输出: 5 + 3 = 8 +} +``` + +使用 `Box<dyn Fn>` 返回闭包 + +```rust +fn make_adder(x: i32) -> Box<dyn Fn(i32) -> i32> { + Box::new(move |y| x + y) +} + +fn main() { + let add_ten = make_adder(10); + println!("10 + 2 = {}", add_ten(2)); // 输出: 10 + 2 = 12 +} +``` + +### 闭包特性(Traits) + +闭包根据其捕获方式自动实现了以下三个特性: + +- **`Fn`**: 不需要修改捕获的变量,闭包可以多次调用。 +- **`FnMut`**: 需要修改捕获的变量,闭包可以多次调用。 +- **`FnOnce`**: 只需要捕获所有权,闭包只能调用一次。 + +```rust +fn call_closure<F>(f: F) +where + F: FnOnce(), +{ + f(); // 只调用一次 +} + +fn main() { + let name = String::from("Rust"); + + // 使用 move 强制捕获所有权 + let print_name = move || println!("Hello, {}!", name); + + call_closure(print_name); + // println!("{}", name); // 若取消注释,将报错,name 的所有权已被移动 +} +``` + + + +# Rust 所有权 + +计算机程序必须在运行时管理它们所使用的内存资源。 + +大多数的编程语言都有管理内存的功能: + +C/C++ 这样的语言主要通过手动方式管理内存,开发者需要手动的申请和释放内存资源。但为了提高开发效率,只要不影响程序功能的实现,许多开发者没有及时释放内存的习惯。所以手动管理内存的方式常常造成资源浪费。 + +Java 语言编写的程序在虚拟机(JVM)中运行,JVM 具备自动回收内存资源的功能。但这种方式常常会降低运行时效率,所以 JVM 会尽可能少的回收资源,这样也会使程序占用较大的内存资源。 + +所有权对大多数开发者而言是一个新颖的概念,它是 Rust 语言为高效使用内存而设计的语法机制。所有权概念是为了让 Rust 在编译阶段更有效地分析内存资源的有用性以实现内存管理而诞生的概念。 + +## 所有权规则 + +所有权有以下三条规则: + +- Rust中的每一个值都有一个变量,称为其所有者。 +- 一次只能有一个所有者。 +- 当所有者不在程序运行范围时,该值将被删除。 + +这三条规则是所有权概念的基础。 + +接下来介绍与所有权概念有关的概念。 + +## 变量范围 + +我们用下面这段程序描述变量范围的概念: + +```rust +{ + // 在声明以前,变量 s 无效 + let s = "variable"; + // 这里是变量 s 的可用范围 +} +// 变量范围已经结束,变量 s 无效 +``` + +变量范围是变量的一个属性,其代表变量的可行域,默认从声明变量开始有效直到变量所在域结束。 + +## 内存和分配 + +如果我们定义了一个变量并给它赋予一个值,这个变量的值存在于内存中。这种情况很普遍。但如果我们需要储存的数据长度不确定(比如用户输入的一串字符串),我们就无法在定义时明确数据长度,也就无法在编译阶段令程序分配固定长度的内存空间供数据储存使用。(有人说分配尽可能大的空间可以解决问题,但这个方法很不文明)。这就需要提供一种在程序运行时程序自己申请使用内存的机制----堆。本章所讲的"内存资源"都指的是堆所占用的内存空间。 + +有分配就有释放,程序不能一直占用某个内存资源。因此决定资源是否浪费的关键因素就是资源有没有及时的释放。 + +我们把字符串样例程序用 C 语言等价编写: + +```c +{ + char *s = strdup("schar"); + free(s); // 释放 s 资源 +} +``` + +很显然,Rust 中没有调用 free 函数来释放字符串 s 的资源(我知道这样在 C 语言中是不正确的写法,因为 "schar" 不在堆中,这里假设它在)。Rust 之所以没有明确释放的步骤是因为在变量范围结束的时候, Rust 编译器自动添加了调用释放资源函数的步骤。这个机制看似很简单了:它不过是帮助程序员在适当的地方添加了一个释放资源的函数调用而已。但这种简单的机制可以有效地解决一个史上最令程序员头疼的编程问题。 + +## 变量与数据交互的方式 + +变量与数据交互方式主要有移动(Move)和克隆(Clone)两种: + +### 移动 + +多个变量可以在 Rust 中以不同的方式与相同的数据交互: + +```rust +let x = 5; +let y = x; +``` + +这个程序将值 5 绑定到变量 x,然后将 x 的值复制并赋值给变量 y。现在栈中将有两个值 5。此情况中的数据是"基本数据"类型的数据,不需要存储在堆中,仅在栈中的数据的"移动"方式是直接复制,这不会花费更长的时间或更多的存储空间。"基本数据"类型有这些: + +- 所有整数类型,例如 i32、u32、i64 等。 +- 布尔类型 bool,值为 true 或 false 。 +- 所有浮点类型,f32 和 f64。 +- 字符类型 char。 +- 仅包含以上类型数据的元组(Tuples)。 + +但如果发生交互的数据在堆中就是另外一种情况: + +```rust +let s1 = String::from("hello"); +let s2 = s1; +``` + +第一步产生一个 String 对象,值为 "hello"。其中 "hello" 可以认为是类似于长度不确定的数据,需要在堆中存储。 + +第二步的情况略有不同(这不是完全真的,仅用来对比参考): + +![img](https://www.runoob.com/wp-content/uploads/2020/04/rust-ownership1.png) + +如图所示:两个 String 对象在栈中,每个 String 对象都有一个指针指向堆中的 "hello" 字符串。在给 s2 赋值时,只有栈中的数据被复制了,堆中的字符串依然还是原来的字符串。 + +前面我们说过,当变量超出范围时,Rust 自动调用释放资源函数并清理该变量的堆内存。但是 s1 和 s2 都被释放的话堆区中的 "hello" 被释放两次,这是不被系统允许的。为了确保安全,在给 s2 赋值时 s1 已经无效了。没错,在把 s1 的值赋给 s2 以后 s1 将不可以再被使用。下面这段程序是错的: + +```rust +let s1 = String::from("hello"); +let s2 = s1; +println!("{}, world!", s1); // 错误!s1 已经失效 +``` + +所以实际情况是: + +![img](https://www.runoob.com/wp-content/uploads/2020/04/rust-ownership2.png) + +s1 名存实亡。 + +### 克隆 + +Rust会尽可能地降低程序的运行成本,所以默认情况下,长度较大的数据存放在堆中,且采用移动的方式进行数据交互。但如果需要将数据单纯的复制一份以供他用,可以使用数据的第二种交互方式——克隆。 + +```rust +fn main() { +    let s1 = String::from("hello"); +    let s2 = s1.clone(); +    println!("s1 = {}, s2 = {}", s1, s2); +} +``` + +运行结果: + +```shell +s1 = hello, s2 = hello +``` + +这里是真的将堆中的 "hello" 复制了一份,所以 s1 和 s2 都分别绑定了一个值,释放的时候也会被当作两个资源。 + +当然,克隆仅在需要复制的情况下使用,毕竟复制数据会花费更多的时间。 + +## 涉及函数的所有权限制 + +对于变量来说这是最复杂的情况了。 + +如果将一个变量当作函数的参数传给其他函数,怎样安全的处理所有权呢? + +下面这段程序描述了这种情况下所有权机制的运行原理: + +```rust +fn main() { +    let s = String::from("hello"); +    // s 被声明有效 + +    takes_ownership(s); +    // s 的值被当作参数传入函数 +    // 所以可以当作 s 已经被移动,从这里开始已经无效 + +    let x = 5; +    // x 被声明有效 + +    makes_copy(x); +    // x 的值被当作参数传入函数 +    // 但 x 是基本类型,依然有效 +    // 在这里依然可以使用 x 却不能使用 s + +} // 函数结束, x 无效, 然后是 s. 但 s 已被移动, 所以不用被释放 + + +fn takes_ownership(some_string: String) { +    // 一个 String 参数 some_string 传入,有效 +    println!("{}", some_string); +} // 函数结束, 参数 some_string 在这里释放 + +fn makes_copy(some_integer: i32) { +    // 一个 i32 参数 some_integer 传入,有效 +    println!("{}", some_integer); +} // 函数结束, 参数 some_integer 是基本类型, 无需释放 +``` + +如果将变量当作参数传入函数,那么它和移动的效果是一样的。 + +### 函数返回值的所有权机制 + +```rust +fn main() { + let s1 = gives_ownership(); + // gives_ownership 移动它的返回值到 s1 + + let s2 = String::from("hello"); + // s2 被声明有效 + + let s3 = takes_and_gives_back(s2); + // s2 被当作参数移动, s3 获得返回值所有权 +} // s3 无效被释放, s2 被移动, s1 无效被释放. + +fn gives_ownership() -> String { + let some_string = String::from("hello"); + // some_string 被声明有效 + + return some_string; + // some_string 被当作返回值移动出函数 +} + +fn takes_and_gives_back(a_string: String) -> String { + // a_string 被声明有效 + + a_string // a_string 被当作返回值移出函数 +} +``` + +## 引用与租借 + +引用(Reference)是 C++ 开发者较为熟悉的概念。 + +如果你熟悉指针的概念,你可以把它看作一种指针。 + +实质上"引用"是变量的间接访问方式。 + +```rust +fn main() { + let s1 = String::from("hello"); + let s2 = &s1; + println!("s1 is {}, s2 is {}", s1, s2); +} +``` + +运行结果: + +``` +s1 is hello, s2 is hello +``` + +**&** 运算符可以取变量的"引用"。 + +当一个变量的值被引用时,变量本身不会被认定无效。因为"引用"并没有在栈中复制变量的值: + +![img](https://www.runoob.com/wp-content/uploads/2020/04/F25111E7-C5D3-464A-805D-D2186A30C8A0.jpg) + +函数参数传递的道理一样: + +```rust +fn main() { + let s1 = String::from("hello"); + + let len = calculate_length(&s1); + + println!("The length of '{}' is {}.", s1, len); +} + +fn calculate_length(s: &String) -> usize { + s.len() +} +``` + +运行结果: + +``` +The length of 'hello' is 5. +``` + +引用不会获得值的所有权。 + +引用只能租借(Borrow)值的所有权。 + +引用本身也是一个类型并具有一个值,这个值记录的是别的值所在的位置,但引用不具有所指值的所有权: + +```rust +fn main() { + let s1 = String::from("hello"); + let s2 = &s1; + let s3 = s1; + println!("{}", s2); +} +``` + +这段程序不正确:因为 s2 租借的 s1 已经将所有权移动到 s3,所以 s2 将无法继续租借使用 s1 的所有权。如果需要使用 s2 使用该值,必须重新租借: + +```rust +fn main() { + let s1 = String::from("hello"); + let mut s2 = &s1; + let s3 = s1; + s2 = &s3; // 重新从 s3 租借所有权 + println!("{}", s2); +} +``` + +这段程序是正确的。 + +既然引用不具有所有权,即使它租借了所有权,它也只享有使用权(这跟租房子是一个道理)。 + +如果尝试利用租借来的权利来修改数据会被阻止: + +```rust +fn main() { + let s1 = String::from("run"); + let s2 = &s1; + println!("{}", s2); + s2.push_str("oob"); // 错误,禁止修改租借的值 + println!("{}", s2); +} +``` + diff --git a/source/_posts/SQL Server.md b/source/_posts/SQL Server.md new file mode 100644 index 0000000..b51d297 --- /dev/null +++ b/source/_posts/SQL Server.md @@ -0,0 +1,23 @@ +--- +title: SQL Server +date: 2022-11-26 14:48:31 +author: 文永达 +top_img: https://gcore.jsdelivr.net/gh/volantis-x/cdn-wallpaper/abstract/B951AE18-D431-417F-B3FE-A382403FF21B.jpeg +--- + +# SQL Server + +## 建库字符集问题 + +```sql +ALTER DATABASE ACT_DEV SET SINGLE_USER WITH ROLLBACK IMMEDIATE; + +ALTER DATABASE ACT_DEV COLLATE Chinese_PRC_CI_AS; + +ALTER DATABASE ACT_DEV SET MULTI_USER; +``` + +## 精度标度 + +精度指数字的位数,标度指小数点后的位数。例如,123.45,精度是5,标度是2,decimal(5, 2) + diff --git a/source/_posts/Spring.md b/source/_posts/Spring.md new file mode 100644 index 0000000..fbcad58 --- /dev/null +++ b/source/_posts/Spring.md @@ -0,0 +1,34 @@ +--- +title: Spring +date: 2023-10-09 14:17:02 +tags: +--- + +# Spring Cloud + +## OpenFeign + +### 引入pom.xml + +```xml +<properties> + <java.version>17</java.version> + <spring-cloud.version>2022.0.1</spring-cloud.version> +</properties> +<dependencyManagement> + <dependencies> + <dependency> + <groupId>org.springframework.cloud</groupId> + <artifactId>spring-cloud-dependencies</artifactId> + <version>${spring-cloud.version}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + </dependencies> +</dependencyManagement> +<dependency> + <groupId>org.springframework.cloud</groupId> + <artifactId>spring-cloud-starter-openfeign</artifactId> +</dependency> +``` + diff --git a/source/_posts/Subversion.md b/source/_posts/Subversion.md new file mode 100644 index 0000000..e9cedfa --- /dev/null +++ b/source/_posts/Subversion.md @@ -0,0 +1,41 @@ +--- +title: Subversion +date: 2025-11-27 09:46:04 +tags: +--- + +# TortoiseSVN + +## 日志缓存问题 + +> 修改日志后,在日志信息里还是显示原来的日志,但是点击编辑日志弹窗内显示的就是修改后的日志。 + +修改已经成功提交到了服务器(这就是为什么点“编辑”能看到新内容),但 TortoiseSVN 的“显示日志”列表为了提高速度,读取的是本地电脑上的**旧缓存数据**,没有去服务器重新拉取。 + +可以按照以下步骤解决这个问题: + +这是一个非常典型的 **TortoiseSVN 日志缓存(Log Cache)** 问题。 + +简单来说,您的修改已经成功提交到了服务器(这就是为什么点“编辑”能看到新内容),但 TortoiseSVN 的“显示日志”列表为了提高速度,读取的是本地电脑上的**旧缓存数据**,没有去服务器重新拉取。 + +您可以按照以下步骤解决这个问题: + +### 方法一:强制刷新(最快解决) + +在“显示日志(Show Log)”的窗口界面中: + +1. 直接按下键盘上的 **`F5`** 键。 +2. 或者点击窗口底部的 **“刷新(Refresh)”** 按钮。 +3. 如果还是不行,尝试按 **`Shift + F5`** 或 **`Ctrl + F5`**(强制从服务器重新拉取所有日志)。 + +通常这样操作后,列表中的文字就会更新为修改后的内容。 + +### 方法二:清除日志缓存(如果方法一无效) + +如果刷新后依然显示旧日志,说明本地缓存文件可能卡住了,需要手动清理: + +1. 在任意文件夹右键,选择 **TortoiseSVN** -> **设置 (Settings)**。 +2. 在左侧菜单中找到 **已保存数据 (Saved Data)**。 +3. 在右侧面板中找到 **日志信息(显示日志对话框) (Log messages)** 这一栏。 +4. 点击旁边的 **“清除 (Clear)”** 按钮。 +5. 点击确定,然后重新打开“显示日志”窗口,TortoiseSVN 会被迫从服务器重新下载最新的日志数据。 diff --git a/source/_posts/Typora.md b/source/_posts/Typora.md new file mode 100644 index 0000000..c324f9d --- /dev/null +++ b/source/_posts/Typora.md @@ -0,0 +1,208 @@ +--- +title: Typora +date: 2022-11-18 14:48:31 +author: 文永达 +top_img: https://gcore.jsdelivr.net/gh/volantis-x/cdn-wallpaper/abstract/B951AE18-D431-417F-B3FE-A382403FF21B.jpeg +--- +# Typora + +## 快捷键 + +### 字体操作快捷键 + +| 功能 | 快捷键 | +| -------- | ----------- | +| 字体加粗 | Ctrl+B | +| 下划线 | Ctrl+U | +| 倾斜 | Ctrl+I | +| 删除线 | Alt+Shift+5 | + +### 插入功能快键键 + +| **功能** | **快键键** | +| ---------------------------- | ------------ | +| 插入图片(本地图片可直接拖入) | Ctrl+Shift+I | +| 插入表格 | Ctrl+T | +| 插入有序列表 | Ctrl+Shift+[ | +| 插入无序列表 | Ctrl+Shift+] | +| 插入超链接 | Ctrl+K | +| 插入代码片 | Ctrl+Shift+` | +| 插入代码块 | Ctrl+Shift+K | +| 插入公式块 | Ctrl+Shift+M | +| 插入引用块 | Ctrl+Shift+Q | + +### 标题段落快捷键 + +| 功能 | 快捷键 | +| --------------------- | -------- | +| 段落(正文) | Ctrl+0 | +| 一级标题 | Ctrl+1 | +| 二级标题 | Ctrl+2 | +| 三–六级标题(以此类推) | Ctrl+3–6 | +| 提升标题级别 | Ctrl+‘+’ | +| 降低标题级别 | Ctrl+‘-’ | + + + +## 设置引用图片存储路径 + +![image-20221118150139161](D:\source\repos\XiaodaBlogSource\source\_posts\Typora\image-20221118150139161.png) + +## Typora添加右键新建Markdown文件 + +### 步骤 + +新建一个txt文本文件,写入: + +```shell +Windows Registry Editor Version 5.00 + +[HKEY_CLASSES_ROOT\.md] +@="Typora.md" +"Content Type"="text/markdown" +"PerceivedType"="text" + +[HKEY_CLASSES_ROOT\.md\ShellNew] +"NullFile"="" + + +``` + +然后修改.txt后缀为.reg的注册表文件 + +然后双击运行 + +## 几点说明 + +这个方法要先安装 `Typora`。如果不安装 `Typora`,只是导入上述注册表,在很久以前是可以生效的,但自从某次 `Windows`更新之后就失效了,之后的 Windows 要求 `[HKEY__ROOT\.md]`项的 `@ 值 xxx`必须对应于注册表项 `[HKEY_CLASSES_ROOT\xxx]`,这里填写 `Typora.md`,是因为 `Typora `已经为我们生成了 `[HKEY_CLASSES_ROOT\Typora.md]`,可以不用再重入导入,这个项的 `@ 值`代表右键新建该类型的名称显示。如果是自定义其他的类型,则必须导入一遍,比如要添加 `.py`类型,一个最简的注册表如下: + +```shell +Windows Registry Editor Version 5.00 + +[HKEY_CLASSES_ROOT\.py] +@="PythonFile" + +[HKEY_CLASSES_ROOT\.py\ShellNew] +"NullFile"="" + +[HKEY_CLASSES_ROOT\PythonFile] +@="Python 脚本" + +``` + +## 可能遇到的问题 + +右键新建markdown文件以后,可能会发现文件有几十MB那么大。 + +解决方法:`win+R`打开“运行”输入 `regedit`打开注册表,打开路径 `\HKEY_CLASSES_ROOT\.md\ShellNew` + +删除多余的文件(尤其是有一个什么Markdown File),只保留如下的两项。然后关闭注册表,即可修复bug + +![img](D:\source\repos\XiaodaBlogSource\source\_posts\Typora\regedit.png) + + + +## 上传图片至阿里云OSS + +模板 + +```json +{ + "picBed": { + "uploader": "aliyun", + "aliyun": { + "accessKeyId": "", + "accessKeySecret": "", + "bucket": "", // 存储空间名 + "area": "", // 存储区域代号 + "path": "img/", // 自定义存储路径 + "customUrl": "", // 自定义域名,注意要加 http://或者 https:// + "options": "" // 针对图片的一些后缀处理参数 PicGo 2.2.0+ PicGo-Core 1.4.0+ + } + }, + "picgoPlugins": {} +} +``` + + +![image-20221121130426162](D:\source\repos\XiaodaBlogSource\source\_posts\Typora\image-20221121130426162.png) + +![image-20221121130857072](D:\source\repos\XiaodaBlogSource\source\_posts\Typora\image-20221121130857072.png) + +## 上传图片至Minio + +### 安装pipgo + +使用 npm 全局安装 + +```shell +npm install picgo -g +``` + +安装成功后,输入以下命令查看是否安装成功 + +```shell +picgo -v +``` + +### 安装Minio插件 + +```shell +picgo install minio +``` + +安装完成后,输入以下命令查看是否安装成功 + +```shell +picgo use plugins +``` + +### 编辑pipgo配置文件 + +通常位于`~/.picgo/config.json` + +```shell +{ + "picBed": { + "current": "minio", + "minio": { + "endpoint": "your-minio-endpoint", // MinIO服务器地址 + "port": 9000, // MinIO端口,默认9000 + "accessKey": "your-access-key", // MinIO访问密钥 + "secretKey": "your-secret-key", // MinIO私有密钥 + "bucket": "your-bucket-name", // 存储桶名称 + "path": "img/", // 存储路径(可选) + "useSSL": false, // 是否使用SSL(默认false) + "customDomain": "", // 自定义域名 + "pathFormat": "" + } + }, + "picgoPlugins": { + "picgo-plugin-minio": true + } +} +``` + +### 测试上传 + +```shell +picgo upload /path/to/your/image.png +``` + +### Typora设置 + +![image-20250416154513069](D:\source\repos\XiaodaBlogSource\source\_posts\Typora\image-20250416154513069.png) + +## 主题 + +### typora-gitbook-theme + +https://github.com/h16nning/typora-gitbook-theme + +### typora-ladder-theme + +https://github.com/guangzhengli/typora-ladder-theme + +### Mdmdt + +[cayxc/Mdmdt: Typora极简文档主题Mdmdt,包含亮色和暗色两种主题,是深度定制的个性化Typora主题;Typora minimalist document theme Mdmdt. Featuring both light and dark themes, it is a deeply customized personalized Typora theme.](https://github.com/cayxc/Mdmdt) diff --git a/source/_posts/Typora/image-20221118150139161.png b/source/_posts/Typora/image-20221118150139161.png new file mode 100644 index 0000000..24c343c Binary files /dev/null and b/source/_posts/Typora/image-20221118150139161.png differ diff --git a/source/_posts/Typora/image-20221121130426162.png b/source/_posts/Typora/image-20221121130426162.png new file mode 100644 index 0000000..dc9da5f Binary files /dev/null and b/source/_posts/Typora/image-20221121130426162.png differ diff --git a/source/_posts/Typora/image-20221121130857072.png b/source/_posts/Typora/image-20221121130857072.png new file mode 100644 index 0000000..74aa4e9 Binary files /dev/null and b/source/_posts/Typora/image-20221121130857072.png differ diff --git a/source/_posts/Typora/image-20250416154513069.png b/source/_posts/Typora/image-20250416154513069.png new file mode 100644 index 0000000..86fefac Binary files /dev/null and b/source/_posts/Typora/image-20250416154513069.png differ diff --git a/source/_posts/Typora/regedit.png b/source/_posts/Typora/regedit.png new file mode 100644 index 0000000..4cd60c7 Binary files /dev/null and b/source/_posts/Typora/regedit.png differ diff --git a/source/_posts/Vim.md b/source/_posts/Vim.md new file mode 100644 index 0000000..3bee338 --- /dev/null +++ b/source/_posts/Vim.md @@ -0,0 +1,204 @@ +--- +title: Vim +date: 2023-09-21 15:00:20 +tags: +--- + +# Vim 的工作模式 + +vim 有6种工作模式。 + +- 普通模式:使用 vim 打开一个文件时默认模式。也叫命令模式,运行用户通过各种命令浏览代码、滚屏等操作。 +- 插入模式:也可以叫做编辑模式,在普通模式下敲击i、a 或 o 就进入插入模式,允许用户通过键盘输入、编辑。 +- 命令行模式:在普通模式下,先输入冒号`:`,接着输入命令 ,就可以通过配置命令对 vim 进行配置了,如改变颜色主题、显示行号等,这些配置命令也可以保存到/etc/vim/vimrc配置文件中,每次打开默认配置执行。 +- 可视化模式:在普通模式下敲击键盘上的 v 键,就进入可视化模式,然后移动光标就可以选中一块文本,常用来完成文本的复制、粘贴、删除等操作。 +- 替换模式:如果我们想修改某个字符,不需要先进入插入模式,删除,然后再输入新的字符,直接在普通模式下,敲击`R`键就可以直接替换。 +- EX模式:类似于命令行模式,可以一次运行多个命令。 + +vim 的各种工作模式可以通过不同的键进行切换,用户统一使用`ESC`键返回到普通模式。 + +## 命令模式 + +**用户刚刚启动vim,便进入里命令模式** + +此状态下敲击键盘动作会被 Vim 识别为命令,而非输入字符,比如我们此时按下`i`,并不会输入一个字符,`i`被当作了一个命令。 + +以下是普通模式常用的几个命令: + +- `i` -- 切换到输入模式,在光标当前位置开始输入文本。 +- `x` -- 删除当前光标所在处的字符。 +- `:` -- 切换到底线命令模式,以在最底一行输入命令。 +- `a` -- 进入插入模式,在光标下一个位置开始输入文本。 +- `o` -- 在当前行的下方插入一个新行,并进入插入模式。 +- `O` -- 在当前行的上方插入一个新行,并进入插入模式。 +- `dd` -- 删除当前行。 +- `yy` -- 复制当前行。 +- `p`(小写)-- 粘贴剪贴板内容到光标下方。 +- `P`(大写)-- 粘贴剪贴板内容到光标上方。 +- `u` -- 撤销上一次操作。 +- `Ctrl + r` -- 重做上一次撤销的操作。 +- `:w` -- 保存文件。 +- `:q` -- 退出 Vim 编辑器 +- `:q!` -- 强制退出 Vim 编辑器,不保存修改。 + +若要编辑文本,只需要启动 Vim,进入了命令模式,按下`i`切换到输入模式即可。 + +命令模式只有一些最基本的命令,因此仍要依靠**底线命令行模式**输入更多命令。 + +## 输入模式 + +在命令模式下按下`i`就进入了输入模式,使用`Esc`键可以返回到普通模式。 + +在输入模式中,可以使用以下按键: + +- **字符按键以及Shifi组合**,输入字符 +- **ENTER**,回车键,换行 +- **BACK SPACE**,退格键,删除光标前一个字符 +- **DEL**,删除键,删除光标后一个字符 +- **方向键**,在文本中移动光标 +- **HOME/END**,移动光标到行首/行尾 +- **Page Up/Page Down**,上/下翻页 +- **Insert**,切换光标输入为输入\替换模式,光标将变成竖线/下划线 +- **ESC**,退出输入模式,切换到命令模式 + +## 底线命令模式 + +在命令模式下按下`:`(英文冒号)就进入了底线命令模式。 + +底线命令模式可以输入单个或多个字符的命令,可用的命令非常多。 + +在底线命令模式中,基本的命令有(已经省略了冒号): + +- :w 保存文件 +- :q 退出 Vim 编辑器 +- :wq 保存文件并退出 Vim 编辑器 +- :q! 强制退出 Vim 编辑器,不保存修改 +- :set nu 开启行号 +- :set nonu 关闭行号 + +按`ESC`键可随时退出底线命令模式。 + +简单的说,我们可以将这三个模式想成底下的图标来表示: + +![img](https://www.runoob.com/wp-content/uploads/2014/07/vim-vi-workmodel.png) + +# Vim 的按键说明 + +## 第一部分:命令模式可用的光标移动、复制粘贴、搜索替换等 + +<center> +<table> + <thead> + <tr> + <th colspan="2">移动光标的方法</th> + </tr> + </thead> + <tbody> + <tr> + <td>h 或 向左箭头键(←)</td> + <td>光标向左移动一个字符</td> + </tr> + <tr> + <td>j 或 向下箭头键(↓)</td> + <td>光标向下移动一个字符</td> + </tr> + <tr> + <td>k 或 向上箭头键(↑)</td> + <td>光标向上移动一个字符</td> + </tr> + <tr> + <td>l 或 向右箭头键(→)</td> + <td>光标向右移动一个字符</td> + </tr> + <tr> + <td colspan="2">如果你将右手放在键盘上的话,你会发现 hjkl 是排列在一起的,因此可以使用这四个按钮来移动光标。如果想要进行多次移动的话,例如向下移动 30 行,可以使用 "30j" 或 "30↓" 的组合按键,亦即加上想要进行的次数(数字)后,按下动作即可!</td> + </tr> + <tr> + <td>Ctrl + f</td> + <td>屏幕『向下』移动一页,相当于 Page Down 按键 (常用)</td> + </tr> + <tr> + <td>Ctrl + b</td> + <td>屏幕『向上』移动一页,相当于 Page Up 按键 (常用)</td> + </tr> + <tr> + <td>Ctrl + d</td> + <td>屏幕『向下』移动半页</td> + </tr> + <tr> + <td>Ctrl + u</td> + <td>屏幕『向上』移动半页</td> + </tr> + <tr> + <td>+</td> + <td>光标移动到非空格符的下一行</td> + </tr> + <tr> + <td>-</td> + <td>光标移动到非空格符的上一行</td> + </tr> + <tr> + <td>n『空格』</td> + <td>那个 n 表示『数字』,例如 20 。按下数字后再按空格键,光标会向右移动这一行的 n 个字符。例如 20『空格』 则光标会向后面移动 20 个字符距离。</td> + </tr> + <tr> + <td>0 或功能键[Home]</td> + <td>这是数字『 0 』:移动到这一行的最前面字符处 (常用)</td> + </tr> + <tr> + <td>$ 或功能键[End]</td> + <td>移动到这一行的最后面字符处(常用)</td> + </tr> + <tr> + <td>H</td> + <td>光标移动到这个屏幕的最上方那一行的第一个字符</td> + </tr> + <tr> + <td>M</td> + <td>光标移动到这个屏幕的中央那一行的第一个字符</td> + </tr> + <tr> + <td>L</td> + <td>光标移动到这个屏幕的最下方那一行的第一个字符</td> + </tr> + <tr> + <td>G</td> + <td>移动到这个档案的最后一行(常用)</td> + </tr> + <tr> + <td>nG</td> + <td>n 为数字。移动到这个档案的第 n 行。例如 20G 则会移动到这个档案的第 20 行(可配合 :set nu)</td> + </tr> + <tr> + <td>gg</td> + <td>移动到这个档案的第一行,相当于 1G 啊! (常用)</td> + </tr> + <tr> + <td>n『 Enter 』</td> + <td>n 为数字。光标向下移动 n 行(常用)</td> + </tr> + </tbody> + <thead> + <tr> + <th colspan="2">搜索替换</th> + </tr> + </thead> +</table> + </center> +# Vim配置文件 + +## 当前用户配置文件(~/.vimrc) + +若在当前用户目录下没有`.vimrc`,则手动新建即可 + +```bash +vim ~/.vimrc +``` + +其中可添加如命令模式中的命令,比如`set nu`,之后再打开vim则默认开启行号 + +``` +set nu +``` + +`:wq`保存退出后,需使用`source ~/.vimrc`使改动生效 diff --git a/source/_posts/Visual Studio.md b/source/_posts/Visual Studio.md new file mode 100644 index 0000000..49180fd --- /dev/null +++ b/source/_posts/Visual Studio.md @@ -0,0 +1,108 @@ +--- +title: Visual Studio +date: 2018-09-30 18:23:38 +type: "tags" +layout: "tags" +--- + +# 快捷键 + +## 注释:Ctrl+K+C + +## 取消注释:Ctrl+K+U + +## 设置断点调试:F9 + +## 撤销:Ctrl+Z + +## 反撤销:Ctrl+Y + +## 定位到当前行的行首:Home + +## 定位到当前行的行尾:End + +## 选中从光标起到航首间的代码:Shift+Home + +## 选中从光标起到行尾间的代码:Shift+End + +## 定位到当前文本编辑器的首行:Ctrl+Home + +## 定位到当前文本编辑器的末行:Ctrl+End + +## 逐过程调试:F10 + +## 逐语句调试:F11 + +## 跳出调试:Shift+F11 + +## 调用智能提示:Alt+→ + +## 快速隐藏或显示当前代码段:Ctrl+M*2 + +## 跳转到指定某一行:Ctrl+G + +## 转小写:Ctrl+U + +## 转大写:Ctrl+Shift+U + +## 快速切换窗口:Ctrl+Tab + +## 回到上一个光标的位置:Ctrl+— + +## 前进到下一个光标的位置:Ctrl+Shift+— + +## 删除整行代码:Ctrl+L + +# 滚动条缩略预览 + +工具—>选项—>文本编辑器—>所有语言—>滚动条—>行为—>使用垂直滚动条的缩略图模式—>源代码概述—>宽 + +# 扩展 + +## Viasfora + +## File Icons + +# IDE 智能提示汉化 + +工具以`dotnet cli`发布,使用`dotnet tool`进行安装 + +```shell +dotnet tool install -g islocalizer +``` + +工具会自动从`github`下载对应的包进行安装(可能需要访问加速)。 +也可以通过`-cc`参数指定内容对照类型 + +- `OriginFirst`: 原始内容在前 +- `LocaleFirst`: 本地化内容在前 +- `None`: 没有对照 + +```shell +islocalizer install auto -m net6.0 -l zh-cn -cc OriginFirst +``` + +## 自定义生成 + +如下示例生成`.net6`的原始内容在前的`zh-cn`本地化包,并使用 `---------` 分隔原文和本地化内容,生成完成后的`包路径`会输出到控制台。 + +```shell +islocalizer build -m net6.0 -l zh-cn -cc OriginFirst -sl '---------' +``` + +可以通过 `islocalizer build -h` 查看更多的构建参数信息。 + +**首次构建过程可能非常缓慢(需要爬取所有的页面),相关文件会被缓存(单zh-cn内容大小约3.5G),再次构建时会比较快;** + +## 安装 + +```shell +islocalizer install {包路径} +``` + +`包路径`为build命令完成后输出的路径。 + +------ + +可以通过 `islocalizer -h` 查看更多的命令帮助。 + diff --git a/source/_posts/VsCode.md b/source/_posts/VsCode.md new file mode 100644 index 0000000..a2d17ec --- /dev/null +++ b/source/_posts/VsCode.md @@ -0,0 +1,125 @@ +--- +title: VsCode +date: 2025-04-16 15:18:28 +tags: +--- + +# 快捷键 + +## 多行编辑:Alt+Click + + + +# 插件 + +## Remote - SSH + +### 配置远程连接Linux密码免登录 + +首先使用 `PuTTY Key Generator`生成ppk + +![image-20250725142159081](https://rustfs.wenyongdalucky.club:443/hexo/image-20250725142159081.png) + +出现进度条,在框内摇晃鼠标,直到进度条满,就可以看到生成的公钥了,私钥是需要保存到本地的,最好保存到`~/.ssh`目录下 + +![image-20250725142313290](https://rustfs.wenyongdalucky.club:443/hexo/image-20250725142313290.png) + +![image-20250725142335929](https://rustfs.wenyongdalucky.club:443/hexo/image-20250725142335929.png) + +![image-20250725142501371](https://rustfs.wenyongdalucky.club:443/hexo/image-20250725142501371.png) + +框内是生成的公钥,可以保存到本地,也可以直接复制到要连接的Linux服务器上的`~/.ssh/authorized_keys`文件内 + +![image-20250725142604044](https://rustfs.wenyongdalucky.club:443/hexo/image-20250725142604044.png) + +因为刚刚保存的私钥是`ppk`格式的,如果是SSH连接用,需要转换一下,`PuTTY Key Generator`同样有这个功能 + +如果是刚生成好的,可以直接点击上方工具栏 Conversions -> Export OpenSSH Key 然后保存到`~/.ssh`目录下。 + +![image-20250725142721334](https://rustfs.wenyongdalucky.club:443/hexo/image-20250725142721334.png) + +如果是要打开以前生成的密钥,File -> Load private Key。然后再重复上述 Conversions -> Export OpenSSH Key 步骤即可。 + +![image-20250725142840387](https://rustfs.wenyongdalucky.club:443/hexo/image-20250725142840387.png) + +编辑配置文件,一般位于`~/.ssh/config` + +```shell +# Read more about SSH config files: https://linux.die.net/man/5/ssh_config +Host <host> + HostName <host_name> + Port 22 + User <user> + IdentityFile C:/Users/user/.ssh/id_rsa + PreferredAuthentications publickey +``` + +在要连接的远程服务器上保存公钥,使用要进行连接的用户进行登录 + +```shell +vim ~/.ssh/authorized_keys +``` + +将秘钥对应的公钥复制上去 + +![image-20250416155023228](https://rustfs.wenyongdalucky.club:443/hexo/image-20250416155023228.png) + +保存即可 + +编辑 **sshd_config** + +```shell +vim /etc/ssh/sshd_config + +# 使用 /PubkeyAuthentication 查找 +# 编辑,取消注释,并将no 改为yes +PubkeyAuthentication yes + +# :wq 保存 +# 重启sshd服务 AlmaLinux 下 +systemctl restart sshd +# ubuntu 下 +sudo systemctl restart ssh +``` + +Windows `~/.ssh` 目录属性中 安全 -> 高级 + +![image-20250416155557565](https://rustfs.wenyongdalucky.club:443/hexo/image-20250416155557565.png) + +需 停用继承,我这里已经停用 + +![image-20250416155636248](https://rustfs.wenyongdalucky.club:443/hexo/image-20250416155636248.png) + +然后点击 添加,选择主体 + +![image-20250416155715122](https://rustfs.wenyongdalucky.club:443/hexo/image-20250416155715122.png) + +选择高级 + +![image-20250416155749520](https://rustfs.wenyongdalucky.club:443/hexo/image-20250416155749520.png) + +点击 立即查找 + +![image-20250416155823198](https://rustfs.wenyongdalucky.club:443/hexo/image-20250416155823198.png) + +选择 Administrator 和 SYSTEM + +![image-20250416155859285](https://rustfs.wenyongdalucky.club:443/hexo/image-20250416155859285.png) + +![image-20250416155914072](https://rustfs.wenyongdalucky.club:443/hexo/image-20250416155914072.png) + +点击两次 确定后,将 基本权限改为 完全控制 + +![image-20250416155949694](https://rustfs.wenyongdalucky.club:443/hexo/image-20250416155949694.png) + +再次点击确定 + +重复上述步骤,将 当前系统登录用户 添加,不需要勾选完全控制 即可 + +# 问题 + +## 解决VSCode使用微软账户同步设置后,再打开需要重新登录的问题 + +在设置中搜索Microsoft-sovereign-cloud: Environment +修改为ChinaCloud + diff --git a/source/_posts/VsCode/image-20250416155023228.png b/source/_posts/VsCode/image-20250416155023228.png new file mode 100644 index 0000000..7a947ba Binary files /dev/null and b/source/_posts/VsCode/image-20250416155023228.png differ diff --git a/source/_posts/VsCode/image-20250416155557565.png b/source/_posts/VsCode/image-20250416155557565.png new file mode 100644 index 0000000..457d0c7 Binary files /dev/null and b/source/_posts/VsCode/image-20250416155557565.png differ diff --git a/source/_posts/VsCode/image-20250416155636248.png b/source/_posts/VsCode/image-20250416155636248.png new file mode 100644 index 0000000..3be9a31 Binary files /dev/null and b/source/_posts/VsCode/image-20250416155636248.png differ diff --git a/source/_posts/VsCode/image-20250416155715122.png b/source/_posts/VsCode/image-20250416155715122.png new file mode 100644 index 0000000..29b9722 Binary files /dev/null and b/source/_posts/VsCode/image-20250416155715122.png differ diff --git a/source/_posts/VsCode/image-20250416155749520.png b/source/_posts/VsCode/image-20250416155749520.png new file mode 100644 index 0000000..c05d309 Binary files /dev/null and b/source/_posts/VsCode/image-20250416155749520.png differ diff --git a/source/_posts/VsCode/image-20250416155823198.png b/source/_posts/VsCode/image-20250416155823198.png new file mode 100644 index 0000000..19f0b7f Binary files /dev/null and b/source/_posts/VsCode/image-20250416155823198.png differ diff --git a/source/_posts/VsCode/image-20250416155859285.png b/source/_posts/VsCode/image-20250416155859285.png new file mode 100644 index 0000000..7243571 Binary files /dev/null and b/source/_posts/VsCode/image-20250416155859285.png differ diff --git a/source/_posts/VsCode/image-20250416155914072.png b/source/_posts/VsCode/image-20250416155914072.png new file mode 100644 index 0000000..400223d Binary files /dev/null and b/source/_posts/VsCode/image-20250416155914072.png differ diff --git a/source/_posts/VsCode/image-20250416155949694.png b/source/_posts/VsCode/image-20250416155949694.png new file mode 100644 index 0000000..5e4907a Binary files /dev/null and b/source/_posts/VsCode/image-20250416155949694.png differ diff --git a/source/_posts/Vue.md b/source/_posts/Vue.md new file mode 100644 index 0000000..7d54b1e --- /dev/null +++ b/source/_posts/Vue.md @@ -0,0 +1,2208 @@ +--- +title: Vue +date: 2021-03-23 10:30:31 +author: 文永达 +--- +# Vue + +## Vite + +官方文档 + +https://cn.vitejs.dev + +### vite.config.js配置 + +#### src设置@别名 + +```javascript +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' + +const { resolve } = require('path') +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [vue()], + resolve:{ + alias:{ + '@': resolve('src') + } + } +}) +``` + +## vite.config.ts 引入 path 模块注意点 + +1. 安装`@types/node` + +2. 在 `tsconfig.node.json`配置 + + ```json + "compilerOptions": { + ... + "allowSyntheticDefaultTmports": true + } + ``` + + +## Vue CLI + +### 安装 + +```shell +npm install -g @vue/cli +yarn global add @vue/cli +vue --version +``` + +### 升级 + +```shell +npm upadte -g @vue/cli +yarn global upgrade --latest @vue/cli +``` + + +### 创建一个项目 + +```shell +vue create hello-world #创建一个名叫 hello-world 的 vue cli项目 +``` + +### 配置全局环境变量 + +.env 全局默认配置文件,不论什么环境都会加载合并 + +.env.development 开发环境下的配置文件 + +.env.production 生产环境下的配置文件 + +属性名必须以VUE_APP_开头 + +.env.development + +```properties +VUE_APP_URL='' +``` + +文件的加载 + +根据启动命令vue会自动加载对应的环境,vue是根据文件名进行加载的,所以上面说“不要乱起名,也无需专门控制加载哪个文件” + +比如执行npm run serve命令,会自动加载.env.development文件,也有可能是npm run dev,这个得看package.json配置的是啥,具体看下面package.json配置 + +可通过process.env属性 (全局属性) + +.env有的,.env.development也有,就会优先.env.development,.env.development没有的,.env有的,就会有.env的 + +### vue.config.js配置 + +#### src设置@别名 + +```javascript +module.exports = { + chainWebpack: config => { + config.resolve.alias + .set('@', resolve("src")) + } +} +``` + +#### 端口设置 + +```javascript +module.exports = { + devServer: { + port: 9090 + } +} +``` + +#### 跨域代理 + +```javascript +module.exports = { + devServer: { + proxy: { + '/api': { + target: 'http://127.0.0.1:9001', + ws: true, + changeOrigin: true, + pathRewrite: { + '^/api': '' + } + } + } + } +} +``` + + + +### package.json配置 + +#### 环境配置 + +```json +{ + "scripts": { + "start": "vue-cli-service serve --open --mode production", + "dev": "vue-cli-service serve --open", + "build": "vue-cli-service build", + "build:prod": "vue-cli-service build --mode production", + "lint": "vue-cli-service lint --fix", + "upload": "vue-cli-service ssh", + "test:unit": "vue-cli-service test:unit", + "test:e2e": "vue-cli-service test:e2e --mode development" + } +} +``` + +scripts中dev 说明 需要用 npm run dev 来启动开发环境 + +如果是serve则是 npm run serve,可以手动添加serve + +```json +"scripts": { + "start": "vue-cli-service serve --open --mode production", + "dev": "vue-cli-service serve --open", + "serve": "vue-cli-service serve --open", + "build": "vue-cli-service build", + "build:prod": "vue-cli-service build --mode production", + "lint": "vue-cli-service lint --fix", + "upload": "vue-cli-service ssh", + "test:unit": "vue-cli-service test:unit", + "test:e2e": "vue-cli-service test:e2e --mode development" + } +``` + +## EditorConfig + +如果使用的是Vscode,先安装插件 `EditorConfig for Visual Studio Code` + + + +# Axios + +## 安装 + +```shell +npm install axios +yarn add axios +``` + +## 创建实例 + +```javascript +// MyAxios.js +import axios from 'axios' +const instance = axios.create({ + baseURL: 'http://localhost:8080', + timeout: 3000 +}) +export default instance +``` + +```javascript +// demoApi.js +import axios from '@/utils/MyAxios' +import Qs from 'qs' +export const findList = () => { + return axios({ + url: '', + method: 'get', + params: { + + } + }) +} +export const add = (data) => { + return axios({ + url: '', + method: 'post', + //axios默认contentType为application/json springmvc默认是application/x-www-form-urlencoded + //需添加请求头 + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + }, + //并且数据需要转换成js对象 + data: Qs.stringify(data) + }) +} +``` + + + +# TypeScript + +## 基础类型 + +### 布尔值 + +```typescript +let isDone: boolean = false; +``` + +### 数字 + +```typescript +let decLiteral: number = 6; +let hexLiteral: number = 0xf00d; +let binaryLiteral: number = 0b1010; +let octalLiteral: number = 0o744; +``` + +### 字符串 + +```typescript +let name: string = "bob"; +name = 'smith'; +``` + +还可以使用*模版字符串*,它可以定义多行文本和内嵌表达式。 这种字符串是被反引号包围( ```),并且以`${ expr }`这种形式嵌入表达式 + +```typescript +let name: string = `Gene`; +let age: number = 37; +let sentence: string = `Hello , my name is ${ name }. +I'll be ${ age + 1 } years old next month.`; +``` + +这与下面定义相同 + +```typescript +let sentence: string = "Hello, my name is " + name + ".\n\n" + + "I'll be " + (age + 1) + " years old next month."; +``` + +### 数组 + +可以在元素类型后面接上`[]`,表示由此类型元素组成的一个数组: + +```typescript +let list: number[] = [1, 2, 3]; +``` + +或者 + +```typescript +let list: Array<number> = [1, 2, 3]; +``` + +### 元组 + +元组类型允许表示一个已知元素类型和类型的数组,各元素的类型不必相同。比如,你可以定义一对值分别为`string`和`number`类型的元组。 + +```typescript +let x: [string, number]; + +x = ['hello', 10]; +``` + +### 枚举 + +使用`enum`类型可以为一组数组赋予友好的名字。 + +```typescript +enum Color {Red, Green, Blue} +let c: Color = Color.Green; +``` + +默认情况下,从`0`开始为元素编号。 你也可以手动的指定成员的数值。 例如,我们将上面的例子改成从 `1`开始编号: + +```typescript +enum Color {Red = 1, Green, Blue} +let c: Color = Color.Green; +``` + +或者,全部都采用手动赋值: + +```typescript +enum Color {Red = 1, Green = 2, Blue = 4} +let c: Color = Color.Green; +``` + +枚举类型提供的一个便利是你可以由枚举的值得到它的名字。 例如,我们知道数值为`2`,但是不确定它映射到`Color`的哪个名字,我们可以查找相应的名字: + +```typescript +enum Color {Red = 1, Green, Blue} +let colorName: string = Color[2]; + +console.log(colorName); // 显示'Green'因为上面代码里它的值是2 +``` + +### Any + +不希望类型检查器对这些值进行检查而是直接让它们通过编译阶段的检查。 + + 那么可以使用 `any`类型来标记这些变量: + +```typescript +let notSure: any = 4; +notSure = "maybe a string instead"; +notSure = false; // okay, definitely a boolean +``` + +在对现有代码进行改写的时候,`any`类型是十分有用的,它允许你在编译时可选择地包含或移除类型检查。 你可能认为 `Object`有相似的作用,就像它在其它语言中那样。 但是 `Object`类型的变量只是允许你给它赋任意值 - 但是却不能够在它上面调用任意的方法,即便它真的有这些方法: + +```typescript +let notSure: any = 4; +notSure.ifItExists(); // okay, ifItExists might exist at runtime +notSure.toFixed(); // okay, toFixed exists (but the compiler doesn't check) + +let prettySure: Object = 4; +prettySure.toFixed(); // Error: Property 'toFixed' doesn't exist on type 'Object'. +``` + +当只知道一部分数据的类型时,`any`类型也是有用的。比如,有一个数组,它包含了不同类型的数据: + +```typescript +let list: any[] = [1, true, "free"]; +console.log(list[1]); // true +list[1] = 100; // 100 +``` + +比较像元组 + +### Void + +`void`类型与`any`类型相反,它表示没有任何类型。当一个函数没有返回值时,通常会见到其返回类型是`void`: + +```typescript +function warnUser(): void { + console.log("This is my warning message"); +} +``` + +声明一个`void`类型的变量没有什么大用,因为你只能为它赋予`undefinded`和`null`: + +```typescript +let unusable: void = undefin +``` + + + +# VueRouter + +# Vuex + +Vuex是一个专为Vue.js应用程序开发的**状态管理模式**。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。 + +状态(state)通俗说就是数据(data)。 + +可用于父子组件数据传值。最主要用于解决兄弟组件数据传值,以往通过将数据存储到localStorage中。 + +## 安装 + +```shell +yarn add vuex +``` + +## state + +state相当于组件中的data,专门用来存放全局的数据 + +在`src`下创建 `store`文件夹 + +创建`index.js`文件 + +```javascript +import { createStore } from 'vuex'; + +export default createStore({ + // state相当于组件中的data,专门用来存放全局的数据 + state: { + num: 0 + }, + // + getters: {}, + // + mutations: { + increment (state) { + state.num++ + } + }, +}) +``` + +修改入口文件`main.js` + +```javascript +import { createApp } from 'vue' +import App from './App.vue' + +// 引入store +import store from '@/store' + +createApp(App).use(store).mount('#app') +``` + +在`src`下创建`views`文件夹 + +创建`About.vue` + +```vue +<template> + <div> + <h2>About 页面的 数字: {{ num }}</h2> + </div> +</template> + +<script> +export default { + // 计算属性 + computed: { + num() { + return this.$store.state.num + } + } +} +</script> + +<style> + +</style> +``` + +创建`Home.vue` + +```vue +<template> + <div> + <h2>Home 页面的 数字: {{ $store.state.num }}</h2> + </div> + +</template> + +<script> +export default { + data() { + return{ + + } + }, + +} +</script> + +<style> + +</style> +``` + +在`components`文件夹下创建`Btn.vue` + +```vue +<template> + <div> + <button @click="changeNum">点击加1</button> + </div> + +</template> + +<script> +export default { + methods: { + changeNum() { + // 改变状态 + 执行mutations下increment + this.$store.commit('increment') + } + } +} +</script> + +<style> + +</style> +``` + + + +修改主组件`App.vue` + +```vue +<script> +// This starter template is using Vue 3 <script setup> SFCs +// Check out https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup +import HelloWorld from './components/HelloWorld.vue' +import Home from '@/views/Home.vue' +import About from '@/views/About.vue' +import Btn from '@/components/Btn.vue' +export default { + components: { + Home, + About, + Btn + }, + data() { + return{ + + } + }, + methods: { + + }, + computed: { + + } + +} +</script> + +<template> + <div> + <Home></Home> + <About></About> + <Btn></Btn> + </div> + +</template> + +<style> +#app { + font-family: Avenir, Helvetica, Arial, sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + text-align: center; + color: #2c3e50; + margin-top: 60px; +} +</style> + +``` + +## getters + +getters相当于组件中的computed,getters是全局的,computed是组件内部的 + +修改`index.js` + +```javascript +// 在store(仓库)下的index.js这份文件,就是用来做状态管理的 +// import Vuex from 'vuex'; +import { createStore } from 'vuex'; + +export default createStore({ + // state相当于组件中的data,专门用来存放全局的数据 + state: { + num: 0 + }, + // getters相当于组件中的computed,getters是全局的,computed是组件内部的 + getters: { + getNum(state) { + return state.num + } + }, + // + mutations: { + increment (state) { + state.num++ + } + }, + + +}) +``` + +修改`About.vue` + +```vue +<template> + <div> + <h2>About 页面的 数字: {{ $store.getters.getNum }}</h2> + </div> +</template> + +<script> +export default { + computed: { + num() { + return this.$store.state.num + } + } +} +</script> + +<style> + +</style> +``` + +## mutations + +mutations相当于组件中的methods,但是它不能使用异步方法(定时器、axios) + +修改`index.js` + +```javascript +// 在store(仓库)下的index.js这份文件,就是用来做状态管理的 +// import Vuex from 'vuex'; +import { createStore } from 'vuex'; + +export default createStore({ + // state相当于组件中的data,专门用来存放全局的数据 + state: { + num: 0 + }, + // getters相当于组件中的computed,getters是全局的,computed是组件内部的 + getters: { + getNum(state) { + return state.num + } + }, + //mutations相当于组件中的methods,但是它不能使用异步方法(定时器、axios) + mutations: { + increment (state, payload) { + state.num+=payload ? payload : 1 + } + }, + + +}) +``` + +修改`Btn.vue` + +```vue +<template> + <div> + <button @click="changeNum(n)">点击加1</button> + <br> + <input v-model.number="n"> + {{ $store.state.num }} + </div> + +</template> + +<script> +export default { + data() { + return{ + n: 2 + } + }, + methods: { + // 调用store中的mutations里的increment方法 + // 传参的话,使用payload + changeNum(n) { + console.log(n) + this.$store.commit('increment',n) + } + } +} +</script> + +<style> + +</style> +``` + +## actions + +actions专门用来处理异步,实际修改状态值的,依然是mutations + +修改`index.js` + +```javascript +// 在store(仓库)下的index.js这份文件,就是用来做状态管理的 +// import Vuex from 'vuex'; +import { createStore } from 'vuex'; + +export default createStore({ + // state相当于组件中的data,专门用来存放全局的数据 + state: { + num: 0 + }, + // getters相当于组件中的computed,getters是全局的,computed是组件内部的 + getters: { + getNum(state) { + return state.num + } + }, + //mutations相当于组件中的methods,但是它不能使用异步方法(定时器、axios) + mutations: { + increase (state, payload) { + state.num += payload ? payload : 1 + }, + clear (state) { + state.num = 0 + }, + decrease (state, payload) { + if (state.num === 0) { + state.num = 0 + } else { + state.num -= payload ? payload : 1 + } + } + }, + // actions专门用来处理异步,实际修改状态值的,依然是mutations + actions: { + decreaseAync (context, num) { + context.commit('decrease', num) + + } + } + +}) +``` + +修改`Btn.vue` + +```vue +<template> + <div> + <button @click="changeNum(n)">点击加1</button> + <button @click="subNum(n)">点击减1</button> + <button @click="clearNum">清空</button> + <br> + <input v-model.number="n"> + </div> + +</template> + +<script> +export default { + data() { + return{ + n: 1 + } + }, + methods: { + // 调用store中的mutations里的increment方法 + // 传参的话,使用payload + changeNum(n) { + this.$store.commit('increase', n) + }, + clearNum() { + this.$store.commit('clear') + }, + subNum(n) { + this.$store.dispatch('decreaseAync', n) + } + } +} +</script> + +<style> + +</style> +``` + +# Pug.js + +Pug(之前称为Jade)是一种简洁而灵活的模板引擎,用于构建HTML。Pug使用缩进和标记替代了常见的HTML标记,这使得它更易于阅读和编写。它也提供了一些功能,如变量、循环和条件语句等,可以帮助开发人员更轻松地构建动态的网页。 + +## 特性 + +### 属性 + +如果一个标签有多个属性,可使用 分行 或 逗号 + +```less +// 1 +el-button(v-if="ifShow" type="size" size="small" @click="doClidk") 点击 +// 2 +el-button(v-if="ifShow",type="size",size="small",@click="doClidk") 点击 +// 3 +el-button(v-if="ifShow" + type="size" + size="small" + @click="doClick") 点击 +``` + +### 注释 + +- 单行 + +```xml +// 一些内容 +p foo +p bar + +<!-- 一些内容 --> +<p>foo</p> +<p>bar</p> +``` + +- 不输出注释 + +```less +//- 这行不会出现在结果中 +p foo +p bar + +<p>foo</p> +<p>bar</p> +``` + +- 块注释 + +```xml +body + // + 一堆 + 文字 + 进行中 + +<body> +<!-- 一堆 + 文字 + 进行中 --> +</body> +``` + + + +## Vue 2集成Pug.js + +Vue CLI安装Pug.js + +```shell +npm i -D pug pug-html-loader pug-plain-loader +``` + +vue.config.js配置 + +```javascript +// vue.config.js +module.exports = { + chainWebpack: config => { + config.module.rule('pug') + .test(/\.pug$/) + .use('pug-html-loader') + .loader('pug-html-loader') + .end() + } +} +``` + +使用前 + +```vue +<template> + <div class="app-container"> + <!-- 搜索栏 --> + <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch"> + <el-form-item label="昵称"> + <el-input + v-model="queryParams.nickName" + placeholder="请输入用户昵称" + clearable + style="..." + @keyup.enter.native="handleQuery" + /> + </el-form-item> + <el-form-item label="状态" prop="status"> + <el-select + v-model="queryParams.status" + placeholder="请选择状态" + clearable + style="..." + > + <el-option + v-for="dict in dict.type.sys_normal_disable" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> + <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button> + </el-form-item> + </el-form> + + <!-- 工具栏按钮 --> + <el-row :gutter="10" class="mb8"> + <el-col :span="1.5"> + <el-button + type="primary" + plain + icon="el-icon-plus" + size="mini" + @click="handleAdd" + >新增</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="success" + plain + icon="el-icon-edit" + size="mini" + :disabled="single" + @click="handleUpdate" + >修改</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="danger" + plain + icon="el-icon-delete" + size="mini" + :disabled="multiple" + @click="handleDelete" + >删除</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="warning" + plain + icon="el-icon-download" + size="mini" + @click="handleExport" + >导出</el-button> + </el-col> + </el-row> + + <!-- 表格 --> + <el-table v-loading="loading" :data="list" @selection-change="handleSelectionChange"> + <el-table-column type="selection" width="55" align="center" /> + <el-table-column prop="userId" v-if="false"></el-table-column> + <el-table-column label="用户昵称" align="center" prop="nickName" /> + <el-table-column label="用户头像" align="center" prop="pic" /> + <el-table-column label="状态" align="center" prop="status"> + <template slot-scope="scope"> + <dict-tag :options="dict.type.sys_normal_disable" :value="scope.row.status" /> +</template> +</el-table-column> +<el-table-column label="注册时间" align="center" prop="userRegtime"></el-table-column> +<el-table-column label="操作" align="center" class-name="small-padding fixed-width"> + <el-button + size="mini" + type="text" + icon="el-icon-edit" + @click="handleUpdate(scope.row)" + v-hasPermi="['system:dict:edit']" + >修改</el-button> + <el-button + size="mini" + type="text" + icon="el-icon-delete" + @click="handleDelete(scope.row)" + v-hasPermi="['system:dict:remove']" + >删除</el-button> +</el-table-column> +</el-table> + +<pagination + v-show="total>0" + :total="total" + :page.sync="queryParams.pageNum" + :limit.sync="queryParams.pageSize" + @pagination="getList" + /> + +<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body> + <el-form ref="form" :model="form" :rules="rules" label-width="80px"> + <el-form-item label="用户头像" prop="pic"> + <!-- <el-input v-model="form.pic" placeholder="请输入" /> --> + </el-form-item> + <el-form-item label="用户昵称" prop="nickName"> + <el-input v-model="form.nickName" placeholder="请输入用户昵称" /> + </el-form-item> + <el-form-item label="状态" size="mini" prop="status"> + <el-radio + v-for="dict in dict.type.sys_normal_disable" + :key="dict.value" + :label="dict.value" + >{{ dict.label }}</el-radio> + </el-form-item> + </el-form> + <div slot="footer" class="dialog-footer"> + <el-button type="primary" @click="submitForm">确 定</el-button> + <el-button @click="cancel">取 消</el-button> + </div> +</el-dialog> +</div> +</template> +``` + + + +使用后 + +```less +<template lang="pug"> + .app-container + // 搜索栏 + el-form(:model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch") + el-form-item(label="昵称") + el-input( + v-model="queryParams.nickName" + placeholder="请输入用户昵称" + clearable + style="..." + @keyup.enter.native="handleQuery" + ) + el-form-item(label="状态" prop="status") + el-select( + v-model="queryParams.status" + placeholder="请选择状态" + clearable + style="..." + ) + el-option( + v-for="dict in dict.type.sys_normal_disable" + :key="dict.value" + :label="dict.label" + :value="dict.value" + ) + el-form-item + el-button(type="primary" icon="el-icon-search" size="mini" @click="handleQuery") 搜索 + el-button(icon="el-icon-refresh" size="mini" @click="resetQuery") 重置 + + // 工具栏按钮 + el-row.mb8(:gutter="10") + el-col(:span="1.5") + el-button( + type="primary" + plain + icon="el-icon-plus" + size="mini" + @click="handleAdd" + ) 新增 + el-col(:span="1.5") + el-button( + type="success" + plain + icon="el-icon-edit" + size="mini" + :disabled="single" + @click="handleUpdate" + ) 修改 + el-col(:span="1.5") + el-button( + type="danger" + plain + icon="el-icon-delete" + size="mini" + :disabled="multiple" + @click="handleDelete" + ) 删除 + el-col(:span="1.5") + el-button( + type="warning" + plain + icon="el-icon-download" + size="mini" + @click="handleExport" + ) 导出 + + // 表格 + el-table(v-loading="loading" :data="list" @selection-change="handleSelectionChange") + el-table-column(type="selection" width="55" align="center") + el-table-column(prop="userId" v-if="false") + el-table-column(label="用户昵称" align="center" prop="nickName") + el-table-column(label="用户头像" align="center" prop="pic") + el-table-column(label="状态" align="center" prop="status") + template(slot-scope="scope") + dict-tag(:options="dict.type.sys_normal_disable" :value="scope.row.status") + el-table-column(label="注册时间" align="center" prop="userRegtime") + el-table-column(label="操作" align="center" class-name="small-padding fixed-width") + el-button( + size="mini" + type="text" + icon="el-icon-edit" + @click="handleUpdate(scope.row)" + v-hasPermi="['system:dict:edit']" + ) 修改 + el-button( + size="mini" + type="text" + icon="el-icon-delete" + @click="handleDelete(scope.row)" + v-hasPermi="['system:dict:remove']" + ) 删除 + + pagination( + v-show="total>0" + :total="total" + :page.sync="queryParams.pageNum" + :limit.sync="queryParams.pageSize" + @pagination="getList" + ) + + el-dialog( + :title="title" + :visible.sync="open" + width="500px" + append-to-body + ) + el-form( + ref="form" + :model="form" + :rules="rules" + label-width="80px" + ) + el-form-item(label="用户头像" prop="pic") + // el-input(v-model="form.pic" placeholder="请输入") + el-form-item(label="用户昵称" prop="nickName") + el-input(v-model="form.nickName" placeholder="请输入用户昵称") + el-form-item(label="状态" size="mini" prop="status") + el-radio( + v-for="dict in dict.type.sys_normal_disable" + :key="dict.value" + :label="dict.value" + ) {{ dict.label }} + .dialog-footer(slot="footer") + el-button(type="primary" @click="submitForm") 确 定 + el-button(@click="cancel") 取 消 +</template> +``` + + + +# Vben Admin + +官方文档 + +https://vvbin.cn/doc-next/ + +# D2Admin + +## API封装 + +/src/api + +### axios实例 + +`service.js` + +### 自定义API + +modules目录 + +`sys.user.api.js` + +```javascript +// import { find, assign } from 'lodash' + +// const users = [ +// { username: 'admin', password: 'admin', uuid: 'admin-uuid', name: 'Admin' }, +// { username: 'editor', password: 'editor', uuid: 'editor-uuid', name: 'Editor' }, +// { username: 'user1', password: 'user1', uuid: 'user1-uuid', name: 'User1' } +// ] +// import Qs from 'qs' +const baseURL = '/admin' +export default ({ service, request, serviceForMock, requestForMock, mock, faker, tools }) => ({ + /** + * @description 登录 + * @param {Object} data 登录携带的信息 + */ + SYS_USER_LOGIN (data = {}) { + // 模拟数据 + // mock + // .onAny('/login') + // .reply(config => { + // const user = find(users, tools.parse(config.data)) + // return user + // ? tools.responseSuccess(assign({}, user, { token: faker.random.uuid() })) + // : tools.responseError({}, '账号或密码不正确') + // }) + // 接口请求 + return request({ + url: baseURL + '/login/login', + method: 'post', + data: data + // data: Qs.stringify(data), + // headers: { + // 'Content-Type': 'application/x-www-form-urlencoded' + // } + + }) + }, + FIND_USER_LIST (data = {}) { + return request({ + url: baseURL + '/user/getAll', + method: 'post', + data: data + }) + }, + REFRESH_MSG () { + return request({ + url: baseURL + '/user/refreshmsg', + method: 'get' + }) + }, + ADD_USER (data = {}) { + return request({ + url: baseURL + '/user/add', + method: 'post', + data: data + }) + }, + UPDATE_USER (data = {}) { + return request({ + url: baseURL + '/user/update', + method: 'put', + data: data + }) + }, + DELETE_USER_BY_ID (data = {}) { + return request({ + url: baseURL + '/user/delete', + method: 'delete', + data: data + }) + } +}) + +``` + +`sys.menu.api.js` + +```javascript +const baseURL = '/admin' +export default ({ request }) => ({ + MENU_CURRENT (data = {}) { + return request({ + url: baseURL + '/menu/get/current/menutree', + method: 'get' + }) + }, + ROUTER_CURRENT (data = {}) { + return request({ + url: baseURL + '/menu/get/current/vuerouter', + method: 'get' + }) + } +}) + +``` + + + +## 登录逻辑 + +登录页面 + +/src/views/system/login/page.vue + +`page.vue` + +```vue +<script> +methods: { +// 通过Vuex 展开action 分发login + ...mapActions('d2admin/account', [ + 'login' + ]), + + // 提交登录信息 + submit () { + this.$refs.loginForm.validate((valid) => { + if (valid) { + // 登录 + // 注意 这里的演示没有传验证码 + // 具体需要传递的数据请自行修改代码 + // 调用action + this.login({ + username: this.formLogin.username, + password: this.formLogin.password + }) + .then(() => { + // 重定向对象不存在则返回顶层路径 + this.$router.replace(this.$route.query.redirect || '/') + }) + } else { + // 登录表单校验失败 + this.$message.error('表单校验失败,请检查') + } + }) + } +</script> +``` + +/src/store/modules/d2admin/modules/account.js + +`account.js` + +```javascript +actions: { + /** + * @description 登录 + * @param {Object} context + * @param {Object} payload username {String} 用户账号 + * @param {Object} payload password {String} 密码 + * @param {Object} payload route {Object} 登录成功后定向的路由对象 任何 vue-router 支持的格式 + */ + async login ({ dispatch }, { + username = '', + password = '', + to = '/' + } = {}) { + // 这里axios请求 + const res = await api.SYS_USER_LOGIN({ username, password }) + // 设置 cookie 一定要存 uuid 和 token 两个 cookie + // 整个系统依赖这两个数据进行校验和存储 + // uuid 是用户身份唯一标识 用户注册的时候确定 并且不可改变 不可重复 + // token 代表用户当前登录状态 建议在网络请求中携带 token + // 如有必要 token 需要定时更新,默认保存一天 + util.cookies.set('uuid', res.uuid) + util.cookies.set('token', res.token) + // 设置 vuex 用户信息 + await dispatch('d2admin/user/set', { name: res.name }, { root: true }) + // 用户登录后从持久化数据加载一系列的设置 + await dispatch('load') + // 此处执行动态菜单 动态路由 + await dispatch('updateCache', { to: to }) + }, +} +``` + +## 动态菜单 + +修改`main.js`入口文件 + +```javascript +new Vue({ + router, + store, + i18n, + render: h => h(App), + created () { + // 处理路由 得到每一级的路由设置 + this.$store.commit('d2admin/page/init', frameInRoutes) + + // 设置顶栏菜单 + // this.$store.commit('d2admin/menu/headerSet', menuHeader) + // 设置侧边栏菜单 + // this.$store.commit('d2admin/menu/asideSet', menuAside) + + // 对于已登录的做菜单,搜索数据处理 + this.$store.dispatch('d2admin/menu/get') + // 初始化菜单搜索功能 修改搜索数据根据侧边栏还是头部栏 + // this.$store.commit('d2admin/search/init', menuAside) + }, +``` + +注释掉以下 mutation + +```javascript + // 设置顶栏菜单 + // this.$store.commit('d2admin/menu/headerSet', menuHeader) + // 设置侧边栏菜单 + // this.$store.commit('d2admin/menu/asideSet', menuAside) +``` + +所有逻辑在Vuex中完成 + +到 /src/store/modules/d2admin/modules/menu.js + +`menu.js` + +```javascript +// 设置文件 +import setting from '@/setting.js' +import api from '@/api' +import { uniqueId } from 'lodash' +// 这是从/src/menu/index.js拿到的方法 负责给菜单空的path加上 d2-menu-empty- #随机数字 需要引入lodash uniqueId +function supplementPath (menu) { + return menu.map(e => ({ + ...e, + path: e.path || uniqueId('d2-menu-empty-'), + ...e.children ? { + children: supplementPath(e.children) + } : {} + })) +} +export default { + namespaced: true, + state: { + // 顶栏菜单 + header: [], + // 侧栏菜单 + aside: [], + // 侧边栏收缩 + asideCollapse: setting.menu.asideCollapse, + // 侧边栏折叠动画 + asideTransition: setting.menu.asideTransition + }, + actions: { + /** + * 设置侧边栏展开或者收缩 + * @param {Object} context + * @param {Boolean} collapse is collapse + */ + async asideCollapseSet ({ state, dispatch }, collapse) { + // store 赋值 + state.asideCollapse = collapse + // 持久化 + await dispatch('d2admin/db/set', { + dbName: 'sys', + path: 'menu.asideCollapse', + value: state.asideCollapse, + user: true + }, { root: true }) + }, + /** + * 切换侧边栏展开和收缩 + * @param {Object} context + */ + async asideCollapseToggle ({ state, dispatch }) { + // store 赋值 + state.asideCollapse = !state.asideCollapse + // 持久化 + await dispatch('d2admin/db/set', { + dbName: 'sys', + path: 'menu.asideCollapse', + value: state.asideCollapse, + user: true + }, { root: true }) + }, + // 以下为生成菜单数据的 action + /** + * 设置菜单 + * @param {*} param0 + * @param {*} aside + */ + async set ({ state, dispatch }, aside) { + aside = supplementPath(Array.from(await api.MENU_CURRENT())) + state.aside = aside + console.log('---set-------') + // 持久化 + await dispatch('d2admin/db/set', { + dbName: 'sys', + path: 'menu.aside', + value: aside, + user: true + }, { root: true }) + // 搜索 + await dispatch('d2admin/search/init', aside, { root: true }) + }, + // 这个是页面刷新用到的 + async get ({ state, dispatch, commit }) { + state.aside = await dispatch('d2admin/db/get', { + dbName: 'sys', + path: 'menu.aside', + defaultValue: [], + user: true + }, { root: true }) + commit('asideSet', state.aside) + // 搜索 + await dispatch('d2admin/search/init', state.aside, { root: true }) + }, + /** + * 设置侧边栏折叠动画 + * @param {Object} context + * @param {Boolean} transition is transition + */ + async asideTransitionSet ({ state, dispatch }, transition) { + // store 赋值 + state.asideTransition = transition + // 持久化 + await dispatch('d2admin/db/set', { + dbName: 'sys', + path: 'menu.asideTransition', + value: state.asideTransition, + user: true + }, { root: true }) + }, + /** + * 切换侧边栏折叠动画 + * @param {Object} context + */ + async asideTransitionToggle ({ state, dispatch }) { + // store 赋值 + state.asideTransition = !state.asideTransition + // 持久化 + await dispatch('d2admin/db/set', { + dbName: 'sys', + path: 'menu.asideTransition', + value: state.asideTransition, + user: true + }, { root: true }) + }, + /** + * 持久化数据加载侧边栏设置 + * @param {Object} context + */ + async asideLoad ({ state, dispatch }) { + // store 赋值 + const menu = await dispatch('d2admin/db/get', { + dbName: 'sys', + path: 'menu', + defaultValue: setting.menu, + user: true + }, { root: true }) + state.asideCollapse = menu.asideCollapse !== undefined ? menu.asideCollapse : setting.menu.asideCollapse + state.asideTransition = menu.asideTransition !== undefined ? menu.asideTransition : setting.menu.asideTransition + } + }, + mutations: { + /** + * @description 设置顶栏菜单 + * @param {Object} state state + * @param {Array} menu menu setting + */ + headerSet (state, menu) { + // store 赋值 + state.header = menu + }, + /** + * @description 设置侧边栏菜单 + * @param {Object} state state + * @param {Array} menu menu setting + */ + asideSet (state, menu) { + // store 赋值 + state.aside = menu + } + } +} + +``` + + + +到 /src/store/modules/d2admin/modules/account.js + +`account.js` + +新增一个action updateCache + +```javascript +updateCache ({ dispatch }, { to = '/' }) { + return new Promise((resolve, reject) => { + // 设置菜单 + dispatch('d2admin/menu/set', {}, { root: true }) + // 设置路由 + api.ROUTER_CURRENT().then(result => { + dispatch('d2admin/router/load', { to: to, focus: true, data: result }, { root: true }) + }) + + resolve() + }) + } +``` + +api请求 + +/src/api/modules/sys.menu.api.js + +`sys.menu.api.js` + +```javascript +const baseURL = '/admin' +export default ({ request }) => ({ + // 菜单请求 + MENU_CURRENT (data = {}) { + return request({ + url: baseURL + '/menu/get/current/menutree', + method: 'get' + }) + }, + // 路由请求 + ROUTER_CURRENT (data = {}) { + return request({ + url: baseURL + '/menu/get/current/vuerouter', + method: 'get' + }) + } +}) + +``` + +响应报文 + +```json +{ + "code": 0, + "msg": "请求成功", + "data": [ + { + "id": 1, + "title": "信评中心", + "enname": "crManager", + "parentId": -1, + "icon": "folder-o", + "sort": 1, + "isEnabled": 1, + "children": [ + { + "id": 2, + "title": "信评工作台", + "enname": "crWorkbench", + "parentId": 1, + "icon": "folder-o", + "sort": 2, + "isEnabled": 1, + "children": [ + { + "id": 3, + "title": "信用评级待办", + "parentId": 2, + "icon": "folder-o", + "sort": 3, + "isEnabled": 1 + } + ] + }, + { + "id": 4, + "title": "信用评级启动", + "parentId": 1, + "icon": "folder-o", + "isEnabled": 1, + "children": [ + { + "id": 5, + "title": "个人评级", + "enname": "cr_person_launch", + "parentId": 4, + "path": "/cr_person_launch", + "component": "cr/cr_launch/cr_person_launch/cr_person_launch", + "icon": "folder-o", + "isEnabled": 1 + }, + { + "id": 6, + "title": "企业评级", + "enname": "cr_business_launch", + "parentId": 4, + "path": "/cr_business_launch", + "component": "cr/cr_launch/cr_business_launch/cr_business_launch", + "icon": "folder-o", + "isEnabled": 1 + } + ] + }, + { + "id": 7, + "title": "信用评级查询", + "parentId": 1, + "icon": "folder-o", + "isEnabled": 1 + }, + { + "id": 8, + "title": "评级模板管理", + "parentId": 1, + "icon": "folder-o", + "isEnabled": 1 + } + ] + }, + { + "id": 9, + "title": "系统管理", + "parentId": -1, + "icon": "folder-o", + "isEnabled": 1, + "children": [ + { + "id": 10, + "title": "用户管理", + "enname": "usertable", + "parentId": 9, + "path": "/usertable", + "component": "user/usertable", + "icon": "folder-o", + "isEnabled": 1 + }, + { + "id": 11, + "title": "菜单管理", + "enname": "menu", + "parentId": 9, + "path": "/menu", + "component": "menu/menu", + "isEnabled": 1 + }, + { + "id": 16, + "title": "权限管理", + "enname": "role", + "parentId": 9, + "path": "/role", + "component": "role/role", + "isEnabled": 1 + } + ] + }, + { + "id": 12, + "title": "演示页面", + "parentId": -1, + "isEnabled": 1, + "children": [ + { + "id": 13, + "title": "页面 1", + "enname": "page1", + "parentId": 12, + "path": "/page1", + "component": "demo/page1", + "isEnabled": 1 + }, + { + "id": 14, + "title": "页面 2", + "enname": "page2", + "parentId": 12, + "path": "/page2", + "component": "demo/page2", + "isEnabled": 1 + }, + { + "id": 15, + "title": "页面 3", + "enname": "page3", + "parentId": 12, + "path": "/page3", + "component": "demo/page3", + "isEnabled": 1 + } + ] + } + ], + "type": "success" +} +``` + + + +# 包管理器 + +## npm包管理器 + +```shell +# 淘宝镜像 +# 永久生效 +npm config set registry https://registry.npmmirror.com +# 查看镜像 +npm config get registry +# 暂时生效 +npm install --registry https://registry.npmmirror.com +``` + +### CentOS7安装node js + +```shell +# 首先下载nodejs安装包 +# 解压 注意这不是gz压缩 +tar -xvf node-v14.18.1-linux-x64.tar.xz +mv node-v14.18.1-linux-x64.tar.xz /usr/node +vim /etc/profile +# node +export NODE_HOME=/usr/node +export PATH=$PATH:$NODE_HOME/bin +source /etc/profile +# 查看node 版本 +node -v +npm -v +# 安装yarn +npm install -g yarn +# 软连接,防止找不到环境 +ln -s "/usr/node/bin/node" "/usr/local/bin/node" +ln -s "/usr/node/bin/npm" "/usr/local/bin/npm" +ln -s "/usr/node/bin/yarn" "/usr/local/bin/yarn" +``` + +## yarn包管理器 + +```shell +# PowerShell yarn : 无法加载文件 C:\Users\Admin\AppData\Roaming\npm\yarn.ps1,因为在此系统因为在此系统上禁止运行脚本。 +# 以管理员方式运行powershell +set-ExecutionPolicy RemoteSigned + +# 查看当前镜像 +yarn config get registry + +# https://registry.yarnpkg.com +# 更换镜像 +yarn config set registry https://registry.yarnpkg.com + +# 查看镜像是否成功 +yarn config get registry + +# 全局安装PTE脚手架 +yarn global add generator-pte-cli +# 安装依赖 +yarn +# 运行项目 +yarn dev # yarn run dev +# 部署 +# 具体看 package.json +yarn deploy +yarn build +# 移除依赖 +yarn remove pte-ui +# 安装依赖 +yarn add pte-ui@2.1.54 +# 查看缓存路径 +yarn cache dir +# 修改缓存路径 +yarn config set cache-folder "E:\\Yarn\\Cache" + + +``` + +### 安装node-sass + +```shell +# 安装gyp +npm install -g node-gyp +npm config set python python2.7 +npm config set msvs_version 2017 +yarn +``` + +### yarn和npm命令对比 + +| npm | yarn | 注释 | +| ------------------------------- | ---------------------------- | --------------------------------- | +| npm init | yarn init | 初始化项目 | +| npm install | yarn | 安装全部依赖 | +| npm install react --save | yarn add react | 安装某个依赖,保存到dependencies | +| npm uninstall react --save | yarn remove react | 移除某个依赖 | +| npm install react --save-dev | yarn add react --dev | 安装某依赖,保存到devDependencies | +| npm update react --save | yarn upgrade react | 更新某个依赖包 | +| npm install react --global | yarn global add react | 全局安装某个依赖 | +| npm install --save react axios | yarn add react axios | 同时安装多个依赖包 | +| npm install [package]@[version] | yarn add [package]@[version] | 安装指定版本的包 | +| npm rebuild | yarn install --force | 重新下载所有包 | + +## + +## pnpm包管理器 + +```shell +# 查看缓存存储位置 +pnpm store path +# 设置缓存存储位置 +pnpm config set store-dir <new path> +# 修改全局源 +pnpm config set registry https://registry.npmmirror.com +# 修改项目源 +pnpm config set registry https://registry.npmmirror.com --save +# 查看当前设置的源 +pnpm config get registry +``` + + + +## 快速删除 node_modules + +利用 npm 包 rimraf 快速删除 `node_modules` 文件夹 + +先全局安装 npm 包 + +```shell +npm install rimraf -g +``` + +使用 + +```shell +rimraf node_modules +``` + + + +# NVM + +> nvm(Node Version Manager)是一个非常有用的工具,可以让您在同一台机器上安装和管理多个 Node.js 版本。 +> +> [nvm-sh/nvm: Node Version Manager - POSIX-compliant bash script to manage multiple active node.js versions (github.com)](https://github.com/nvm-sh/nvm?tab=readme-ov-file#installing-and-updating) + +**nvm 常用命令:** + +```shell +# 查看 nvm 版本 +nvm --version + +# 列出所有可安装的 Node.js 版本 +nvm list-remote +# Windows 上使用 +nvm list available + +# 安装最新的 LTS 版本 +nvm install --lts + +# 安装特定版本 +nvm install 18.17.0 +nvm install 16.20.1 + +# 列出已安装的版本 +nvm list +# 或 +nvm ls + +# 切换到特定版本 +nvm use 18.17.0 + +# 设置默认版本 +nvm alias default 18.17.0 + +# 查看当前使用的版本 +nvm current + +# 卸载特定版本 +nvm uninstall 16.20.1 + +# 查看当前使用的npm的目录 +npm prefix -g +``` + +# npx + +npm大家都知道,是node的包管理器,npx虽然也见过,但似乎较少用过,那npx到底是什么呢? + +npx是npm5.2.0版本新增的一个工具包,定义为npm包的执行者,相比npm,npx会自动安装依赖包并执行某个命令。 + +假如我们要用create-react-app脚手架创建一个react项目,常规的做法是先安装create-react-app,然后才能使用create-react-app执行命令进行项目创建。 + +```shell +// 第一步 +npm i -g create-react-app +// 第二步 +create-react-app my-react-app +``` + +有了npx后,我们可以省略安装create-react-app这一步。 + +```shell +// 使用npx +npx create-react-app my-react-app +``` + +npx会在当前目录下的`./node_modules/.bin`里去查找是否有可执行的命令,没有找到的话再从全局里查找是否有安装对应的模块,全局也没有的话就会自动下载对应的模块,如上面的create-react-app,npx会将create-react-app下载到一个临时目录,用完即删,不会占用本地资源。 + +> npm自带npx,可以直接使用,如果没有可以手动安装一下:npm i -g npx + +## npx命令参数 + +### --no-install + +--no-install 告诉npx不要自动下载,也意味着如果本地没有该模块则无法执行后续的命令。 + +```shell +npx --no-install create-react-app my-react-app + +// not found: create-react-app +``` + +### --ignore-existing + +--ignore-existing 告诉npx忽略本地已经存在的模块,每次都去执行下载操作,也就是每次都会下载安装临时模块并在用完后删除。 + +### -p + +-p 用于指定npx所要安装的模块,它可以指定某一个版本进行安装: + +```shell +npx -p node@12.0.0 node index.js +``` + +-p 还可以用于同时安装多个模块: + +```shell +npx -p lolcatjs -p cowsay [command] +``` + +### -c + +-c 告诉npx所有命令都用npx解释。 + +```shell +npx -p lolcatjs -p cowsay 'cowsay hello | lolcatjs' +``` + +这样运行会报错,因为第一项命令cowsay hello默认有npx解释,但第二项命令localcatjs会有shell解释,此时lolcatjs并没有全局安装,所以就报错了。这时候可以用 -c 参数来解决。 + +```shell +npx -p lolcatjs -p cowsay -c 'cowsay hello | lolcatjs' +``` + + + +# 加密解密 + +## jsencrypt + +### 安装 + +```shel +npm install jsencrypt --save-dev +``` + +引入 + +```javascript +import jsencrypt from 'jsencrypt' +``` + +# Chrome 插件开发 + +## 项目配置 + +```typescript +import pkg from "../package.json"; + +const manifest: chrome.runtime.Manifest = { + // 描述manifest文件的版本号,必须为3 + manifest_version: 3, + // 描述插件的名称。这个属性就是配置我们插件名称,用于显示在插件列表 + name: pkg.name, + // 描述插件的版本号。 + version: pkg.version, + // 描述插件的简要说明。 + description: pkg.description, + // 允许使用扩展的域名 + host_permissions: ["*://*/*"], + background: { + service_worker: "src/entries/background/main.ts", + }, + // 描述插件图标的大小和文件路径。 + icons: { + "16": "images/icon16.png", + "24": "images/icon24.png", + "32": "images/icon32.png", + "48": "images/icon48.png", + }, + action: { + default_popup: "index.html", + // default_icon: { + // "16": "images/icon16.png", + // "24": "images/icon24.png", + // "32": "images/icon32.png", + // "48": "images/icon48.png", + // }, + }, + options_ui: { + page: "src/entries/options/index.html", + open_in_tab: false, + }, + // activeTab: 当扩展卡选项被改变需要重新获取新的权限 + // scripting: 使用 chrome.scripting API 在不同上下文中执行脚本 + // tabs: 操作选项卡api(改变位置等) + // storage: 访问localstorage/sessionStorage权限 + // declarativeContent: 使用 chrome.declarativeContent API 根据网页内容执行操作,而无需读取网页内容的权限。 + permissions: ["activeTab", "scripting", "declarativeContent: "], + + content_security_policy: { + extension_pages: "script-src 'self'; object-src 'self';", + }, +}; + +export default manifest; + +``` + +## 内容脚本 + +需要操作dom,并且不需要一直在后台运行,只需要再打开网页的时候运行 + +使用内容脚本(`content_scripts`)的方式运行插件即可 + +内容脚本(`content_scripts`)的特性: + +- 在页面打开,或者页面加载结束,或者页面空闲的时候注入 +- 共享页面dom,也就是说可以操作页面的dom +- JS隔离的,插件中的js定义并不会影响页面的js,也不能引用页面中的js变量、函数 + +开始使用`content_scripts`: + +`content_scripts` 有多种使用方式: + +1. 静态注入。在`manifest.json`文件中声明 +2. 动态注入。`chrome.scripting.registerContentScripts` +3. 编码注入。`chrome.scripting.executeScript` + +一般使用静态注入。 + +在`manifest.ts`文件中添加一下`content_scripts`配置 + +```typescript + content_scripts: [ + // { + // matches: ["http://192.168.0.88:8080/kaoqin-server/login*"], + // js: ["src/entries/content/kaoqin-server.js"], + // run_at: "document_end", + // }, + { + //"matches": ["http://*/*", "https://*/*"], + // "<all_urls>" 表示匹配所有地址 + matches: ["<all_urls>"], + // 多个JS按顺序注入 + js: ["src/entries/content/content-script.js"], + // 代码注入的时间,可选值: "document_start", "document_end", or "document_idle",最后一个表示页面空闲时,默认document_idle + run_at: "document_end", + }, + ], +``` + +`content_scripts`还有动态注入的方式,其实就是通过调用api的方法来注入,如下示例代码: + +```typescript +chrome.scripting + .registerContentScripts([{ + id: "session-script", + js: ["content.js"], + persistAcrossSessions: false, + matches: ["*://example.com/*"], + runAt: "document_start", + }]) + .then(() => console.log("registration complete")) + .catch((err) => console.warn("unexpected error", err)) +``` + +动态注入可以scripts注入的时机更可控、或者可以更新、删除content_scripts。 + +`content_scripts`属性是一个数组,也就是说我们可以配置多个脚本规则,数组的每个元素包含多个属性: + +- matches 指定此内容脚本将被注入到哪些页面。必填 +- js 要注入匹配页面的 JavaScript 文件列表。选填 +- css 要注入匹配页面的 CSS 文件列表。选填 +- run_at 指定何时应将脚本注入页面。有三种类型,`document_start`,`document_end`,`document_idle`。默认为document_idle。选填 + +## 页面通信 + +> 由于`content_scripts`是在网页中运行的,而非在扩展的上下文中,因此它们通常需要某种方式与扩展的其余部分进行通信。 扩展页面(`options_page,bakcground,popup`)和内容脚本(`content_scripts`)之间的通信通过使用消息传递进行。 任何一方都可以侦听从另一端发送的消息,并在同一通道上做出响应。消息可以包含任何有效的 JSON 对象(空值、布尔值、数字、字符串、数组或对象)。 + +![img](Vue/16e29d0dea8191b9tplv-t2oaga2asx-jj-mark3024000q75.webp) + +![img](Vue/16e29ccb69463a4dtplv-t2oaga2asx-jj-mark3024000q75.webp) + +### 发送 + +从内容脚本(`content_scripts`) 发送到 扩展页面(`options_page,bakcground,popup`),代码示例: + +```javascript +(async () => { + const response = await chrome.runtime.sendMessage({greeting: "hello"}); + console.log(response); +})(); +``` + +从扩展页面(`options_page,bakcground,popup`)发送到 内容脚本(`content_scripts`) 代码示例: + +```javascript +(async () => { + //获取当前的tab页面 + const [tab] = await chrome.tabs.query({active: true, lastFocusedWindow: true}); + const response = await chrome.tabs.sendMessage(tab.id, {greeting: "hello"}); + console.log(response); +})(); +``` + +### 接受 + +接收消息的方法都是一样的,通过`runtime.onMessage`事件侦听器来处理消息 + +```javascript +chrome.runtime.onMessage.addListener( + function(request, sender, sendResponse) { + console.log(sender.tab ? + "from a content script:" + sender.tab.url : + "from the extension"); + if (request.greeting === "hello") + //处理完消息后、通知发送方 + sendResponse({farewell: "goodbye"}); + } +); +``` + diff --git a/source/_posts/Vue/16e29ccb69463a4dtplv-t2oaga2asx-jj-mark3024000q75.webp b/source/_posts/Vue/16e29ccb69463a4dtplv-t2oaga2asx-jj-mark3024000q75.webp new file mode 100644 index 0000000..164ea33 Binary files /dev/null and b/source/_posts/Vue/16e29ccb69463a4dtplv-t2oaga2asx-jj-mark3024000q75.webp differ diff --git a/source/_posts/Vue/16e29d0dea8191b9tplv-t2oaga2asx-jj-mark3024000q75.webp b/source/_posts/Vue/16e29d0dea8191b9tplv-t2oaga2asx-jj-mark3024000q75.webp new file mode 100644 index 0000000..04a3007 Binary files /dev/null and b/source/_posts/Vue/16e29d0dea8191b9tplv-t2oaga2asx-jj-mark3024000q75.webp differ diff --git a/source/_posts/Vue3.md b/source/_posts/Vue3.md new file mode 100644 index 0000000..e9aee43 --- /dev/null +++ b/source/_posts/Vue3.md @@ -0,0 +1,538 @@ +--- +title: Vue3 +date: 2023-11-18 10:30:31 +author: 文永达 +--- +# 响应式 + +在Vue 3中,响应式是通过`reactive`函数和`ref`函数实现的。`reactive`函数用于创建一个响应式对象,而`ref`函数用于创建一个包装对象,将基本类型的值转换为响应式对象。 + +具体来说,`reactive`函数接收一个普通对象作为参数,并返回一个响应式对象,如下所示: + +```ts +import { reactive } from 'vue'; + +const state = reactive({ + count: 0 +}); +``` + +在这个例子中,我们使用`reactive`函数创建了一个响应式对象`state`,它包含一个属性`count`,初始值为0。 + +在组件中,你可以使用`setup`函数来访问响应式对象,如下所示: + +```vue +<template> + <div> + <p>Count: {{ state.count }}</p> + <button @click="increment">Increment</button> + </div> +</template> + +<script> +import { reactive } from 'vue'; + +export default { + setup() { + const state = reactive({ + count: 0 + }); + + const increment = () => { + state.count++; + }; + + return { + state, + increment + }; + } +} +</script> +``` + +在这个例子中,我们在`setup`函数中访问了响应式对象`state`,并将它绑定到模板中。当点击按钮时,我们调用`increment`函数来增加`state.count`的值。由于`state`是响应式对象,当`state.count`的值发生变化时,模板中的内容也会自动更新。 + +除了`reactive`函数外,Vue 3还提供了`ref`函数,用于将基本类型的值转换为响应式对象。例如,你可以使用`ref`函数来创建一个响应式的计数器,如下所示: + +```ts +import { ref } from 'vue'; + +const count = ref(0); +``` + +在这个例子中,我们使用`ref`函数创建了一个响应式对象`count`,它的初始值为0。 + +在组件中,你可以使用`setup`函数来访问响应式对象,如下所示: + +```vue +<template> + <div> + <p>Count: {{ count }}</p> + <button @click="increment">Increment</button> + </div> +</template> + +<script> +import { ref } from 'vue'; + +export default { + setup() { + const count = ref(0); + + const increment = () => { + count.value++; + }; + + return { + count, + increment + }; + } +} +</script> +``` + +在这个例子中,我们在`setup`函数中访问了响应式对象`count`,并将它绑定到模板中。当点击按钮时,我们调用`increment`函数来增加`count.value`的值。由于`count`是响应式对象,当`count.value`的值发生变化时,模板中的内容也会自动更新。 + +总之,Vue 3中的响应式是通过`reactive`函数和`ref`函数实现的。`reactive`函数用于创建一个响应式对象,而`ref`函数用于将基本类型的值转换为响应式对象。你可以在`setup`函数中访问响应式对象,并将它们绑定到模板中。 + +# 依赖注入 + +Vue 3中的依赖注入是通过provide和inject实现的。provide和inject是一对API,用于在父组件和子组件之间共享数据。具体来说,provide用于在父组件中提供数据,而inject用于在子组件中注入数据。 + +在父组件中,你可以使用provide来提供数据,如下所示: + +```vue +<script> +import { provide } from 'vue'; + +export default { + setup() { + const data = 'Hello, World!'; + provide('data', data); + } +} +</script> +``` + +在子组件中,你可以使用inject来注入数据,如下所示: + +```vue +<script> +import { inject } from 'vue'; + +export default { + setup() { + const data = inject('data'); + console.log(data); // 输出:Hello, World! + } +} +</script> +``` + +在这个例子中,父组件提供了一个名为'data'的数据,子组件通过inject来注入这个数据。注意,子组件中的inject必须在setup函数中使用,而且必须在模板中使用才能生效。 + +总之,Vue 3中的依赖注入是通过provide和inject实现的,它可以帮助你在父组件和子组件之间共享数据。 + +# 插槽 + +## 默认插槽 + +Vue 3中的插槽是通过`<slot>`元素实现的。插槽允许你在组件中定义一些占位符,然后在使用组件时填充这些占位符。具体来说,你可以在组件中使用`<slot>`元素来定义插槽,如下所示: + +```vue +<template> + <div> + <h2>{{ title }}</h2> + <slot></slot> + </div> +</template> +``` + +在这个例子中,我们定义了一个名为`title`的属性和一个插槽。插槽使用了`<slot>`元素,它表示这里是一个插槽占位符,可以在使用组件时填充内容。 + +在使用组件时,你可以在组件标签中填充内容,如下所示: + +```vue +<template> + <div> + <my-component title="Hello, World!"> + <p>This is the content of the slot.</p> + </my-component> + </div> +</template> +``` + +在这个例子中,我们使用了`<my-component>`组件,并在组件标签中填充了一个`<p>`元素。这个`<p>`元素就是填充了组件中定义的插槽。 + +除了默认插槽外,Vue 3还支持具名插槽和作用域插槽。具名插槽允许你定义多个插槽,并在使用组件时指定要填充哪个插槽。作用域插槽允许你将数据传递给插槽内容,以便在插槽中使用。 + +总之,Vue 3中的插槽是通过`<slot>`元素实现的,它允许你在组件中定义占位符,并在使用组件时填充内容。除了默认插槽外,Vue 3还支持具名插槽和作用域插槽。 + +## 具名插槽 + +在Vue 3中,具名插槽允许你定义多个插槽,并在使用组件时指定要填充哪个插槽。具名插槽可以通过在`<slot>`元素上添加`name`属性来定义,如下所示: + +```vue +<template> + <div> + <slot name="header"></slot> + <slot></slot> + <slot name="footer"></slot> + </div> +</template> +``` + +在这个例子中,我们定义了三个插槽,其中第一个和第三个是具名插槽,分别使用了`name`属性来定义。第二个插槽没有使用`name`属性,它是默认插槽。 + +在使用组件时,你可以使用`v-slot`指令来指定要填充哪个插槽,如下所示: + +```vue +<template> + <div> + <my-component> + <template v-slot:header> + <h1>This is the header slot.</h1> + </template> + <p>This is the default slot.</p> + <template v-slot:footer> + <p>This is the footer slot.</p> + </template> + </my-component> + </div> +</template> +``` + +在这个例子中,我们使用了`<my-component>`组件,并在组件标签中填充了三个插槽。使用`v-slot`指令来指定要填充哪个插槽,语法为`v-slot:name`,其中`name`是插槽的名称。 + +除了`v-slot`指令外,Vue 3还支持另一种简写语法`#`,如下所示: + +```vue +<template> + <div> + <my-component> + <template #header> + <h1>This is the header slot.</h1> + </template> + <p>This is the default slot.</p> + <template #footer> + <p>This is the footer slot.</p> + </template> + </my-component> + </div> +</template> +``` + +在这个例子中,我们使用了`#`来代替`v-slot`指令,语法更加简洁。 + +总之,Vue 3中的具名插槽允许你定义多个插槽,并在使用组件时指定要填充哪个插槽。你可以使用`v-slot`指令或`#`来指定要填充哪个插槽。 + +## 作用域插槽 + +在Vue 3中,作用域插槽允许你将数据传递给插槽内容,以便在插槽中使用。作用域插槽可以通过在`<slot>`元素上添加`v-bind`指令来定义,如下所示: + +```vue +<template> + <div> + <slot v-bind:user="user"></slot> + </div> +</template> +``` + +在这个例子中,我们定义了一个作用域插槽,并使用了`v-bind`指令来将`user`数据传递给插槽内容。 + +在使用组件时,你可以在插槽标签中使用`v-slot`指令来定义插槽,并在插槽内容中使用传递的数据,如下所示: + +```vue +<template> + <div> + <my-component> + <template v-slot:default="slotProps"> + <p>{{ slotProps.user.name }}</p> + <p>{{ slotProps.user.age }}</p> + </template> + </my-component> + </div> +</template> +``` + +在这个例子中,我们使用了`<my-component>`组件,并在组件标签中填充了一个作用域插槽。使用`v-slot`指令来定义插槽,并使用`default`作为插槽的名称。在插槽内容中,我们可以通过`slotProps`来访问传递的数据。 + +除了`v-slot`指令外,Vue 3还支持另一种简写语法`#`,如下所示: + +```vue +<template> + <div> + <my-component> + <template #default="slotProps"> + <p>{{ slotProps.user.name }}</p> + <p>{{ slotProps.user.age }}</p> + </template> + </my-component> + </div> +</template> +``` + +在这个例子中,我们使用了`#`来代替`v-slot`指令,语法更加简洁。 + +总之,Vue 3中的作用域插槽允许你将数据传递给插槽内容,以便在插槽中使用。你可以在`<slot>`元素上使用`v-bind`指令来定义作用域插槽,并在插槽标签中使用`v-slot`指令或`#`来定义插槽,并在插槽内容中使用传递的数据。 + +# 比较重要的知识点总结 + +## 对vue.js的响应式数据的理解 + +### Vue 2.x + +**对象类型**:通过`object.defineProperty()`对属性的读取、修改进行拦截(数据劫持) + +**数组类型**:通过重写更新数组的一系列方法来实现拦截(对数组的变更方法进行了包裹) + +![图片](https://mmbiz.qpic.cn/sz_mmbiz_png/BOlSgLkrCDYqyTILUJ6mZmQ4qviaPgxpAaxTicvl4Ac1015TlUpn1HtF31qOwxu9AwqwTepicgyrFYoKIhMh3RObg/640?wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1) + +- 当你把一个普通的JavaScript对象传入Vue实例作为**data**选项,Vue将遍历此对象所有的property,并使用**Object.defineProperty**把这些property全部转为**getter/setter**。 + + - **Object.defineProperty**是ES5中一个无法shim的特性,这也就是Vue不支持IE8以及更低版本浏览器的原因。 + +- 当使用这些数据属性是,会进行依赖收集(收集到当前组件的watcher) + - 每个组件都对应一个watcher实例,它会在组件渲染的过程中把“接触”过的数据记录为依赖之后当依赖项的setter触发时,会通知watcher,从而使它关联的组件重新渲染 + +**存在问题**: + +- 新增属性、删除属性,界面不会更新 +- 直接通过下标修改数组,界面不会自动更新 + +### Vue 3.x + +**通过Proxy(代理)**:拦截对象中任意属性的变化,包括:属性值的读写,属性的增加,属性的删除等。 + +**通过Reffect(反射)**:对源对象的属性进行操作 + +`Proxy`是 ECMAScript 6 中新增的属性。 + +Proxy对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。 + +被Proxy代理虚拟化的对象。它常被作为代理得存储后端。根据目标验证关于对象不可扩展性或不可配置属性的不变量(保持不变的语义)。 + +语法: + +```javascript +const p = new Proxy(target, handler) +``` + +参数: + +target: + +要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。 + +handler: + +一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。 + +# 组合式函数(Hook) + +## 概述 + +在 Vue 应用的概念中,“组合式函数”(Composables)是一个利用 Vue 的组合式 API 来封装和复用**有状态逻辑**的函数。 + +当构建前端应用时,我们常常需要复用公共任务的逻辑。例如为了在不同地方格式化时间,我们可能会抽取一个可复用的日期格式化函数。这个函数封装了**无状态的逻辑**:它在接受一些输入后立刻返回所期望的输出。复用无状态逻辑的库很多,比如你可能已经用过的lodash或是date-fns。 + +相比之下,有状态逻辑负责管理会随时间而变化的状态。一个简单的例子是跟踪当前鼠标在页面中的位置。在实际应用中,也可能是像触摸手势或与数据库的连接状态这样的更复杂的逻辑。 + +## 约定和最佳实践 + +### 命名 + +组合式函数约定用驼峰命名法命名,并以“use”作为开头。 + +### 输入参数 + +即便不依赖于 ref 或 getter 的响应性,组合式函数也可以接受它们作为参数。如果你正在编写一个可能被其他开发者使用的组合式函数,最好处理一下输入参数是 ref 或 getter 而非原始值的情况。可以利用`toValue()`工具函数来实现: + +```js +import { toValue } from 'vue' + +function useFeature(maybeRefOrGetter) { + // 如果 maybeRefOrGetter 是一个 ref 或 getter, + // 将返回它的规范化值。 + // 否则原样返回。 + const value = toValue(maybeRefOrGetter) +} +``` + +如果你的组合式函数在输入参数是 ref 或 getter 的情况下创建了响应式 effect,为了让它能够被正确追踪,请确保要么使用`watch()`显式地监视 ref 或 getter,要么在`watchEffect()`中调用`toValue()`。 + +## 封装下拉框Hook + +```ts +import { onMounted, reactive, ref } from 'vue'; +// 定义下拉框接收的数据格式 +export interface SelectOption { + value: string; + label: string; + disabled?: boolean; + key?: string; +} +// 定义入参格式 +interface FetchSelectProps { + apiFun: () => Promise<any[]>; +} + +export function useFetchSelect(props: FetchSelectProps) { + const { apiFun } = props; + + const options = ref<SelectOption[]>([]); + + const loading = ref(false); + + /* 调用接口请求数据 */ + const loadData = () => { + loading.value = true; + options.value = []; + return apiFun().then( + (data) => { + loading.value = false; + options.value = data; + return data; + }, + (err) => { + // 未知错误,可能是代码抛出的错误,或是网络错误 + loading.value = false; + options.value = [ + { + value: '-1', + label: err.message, + disabled: true, + }, + ]; + // 接着抛出错误 + return Promise.reject(err); + } + ); + }; + + // onMounted 中调用接口 + onMounted(() => { + loadData(); + }); + + return reactive({ + options, + loading, + }); +} +``` + +组件调用 + +```vue +<script setup name="DDemo" lang="ts"> + import { useFetchSelect } from './hook'; + + // 模拟调用接口 + function getRemoteData() { + return new Promise<any[]>((resolve, reject) => { + setTimeout(() => { + // 模拟接口调用有概率出错 + if (Math.random() > 0.5) { + resolve([ + { + key: 1, + name: '苹果', + value: 1, + }, + { + key: 2, + name: '香蕉', + value: 2, + }, + { + key: 3, + name: '橘子', + value: 3, + }, + ]); + } else { + reject(new Error('不小心出错了!')); + } + }, 3000); + }); + } + + // 将之前用的 options,loading,和调用接口的逻辑都抽离到hook中 + const selectBind = useFetchSelect({ + apiFun: getRemoteData, + }); +</script> + +<template> + <div> + <!-- 将hook返回的接口,通过 v-bind 绑定给组件 --> + <a-select v-bind="selectBind" /> + </div> +</template> +``` + +## Loading状态hook + +```ts +import { Ref, ref } from 'vue'; + +type TApiFun<TData, TParams extends Array<any>> = (...params: TParams) => Promise<TData>; + +interface AutoRequestOptions { + // 定义一下初始状态 + loading?: boolean; + // 接口调用成功时的回调 + onSuccess?: (data: any) => void; +} + +type AutoRequestResult<TData, TParams extends Array<any>> = [Ref<boolean>, TApiFun<TData, TParams>]; + +/* 控制loading状态的自动切换hook */ +export function useAutoRequest<TData, TParams extends any[] = any[]>(fun: TApiFun<TData, TParams>, options?: AutoRequestOptions): AutoRequestResult<TData, TParams> { + const { loading = false, onSuccess } = options || { loading: false }; + + const requestLoading = ref(loading); + + const run: TApiFun<TData, TParams> = (...params) => { + requestLoading.value = true; + return fun(...params) + .then((res) => { + onSuccess && onSuccess(res); + return res; + }) + .finally(() => { + requestLoading.value = false; + }); + }; + + return [requestLoading, run]; +} +``` + +组件调用 + +```vue +<script setup name="Index" lang="ts"> +import { useAutoRequest } from "./hook"; +import { Button } from "ant-design-vue"; +import { submitApi } from "@/api"; + +const [loading, submit] = useAutoRequest(submitApi); + +function onSubmit() { + submit("aaa").then((res) => { + console.log("res", res); + }); +} +</script> + +<template> + <div class="col"> + <Button :loading="loading" @click="onSubmit">提交</Button> + </div> +</template> +``` + diff --git a/source/_posts/Windows-Server.md b/source/_posts/Windows-Server.md new file mode 100644 index 0000000..b9df562 --- /dev/null +++ b/source/_posts/Windows-Server.md @@ -0,0 +1,137 @@ +--- +title: Windows Server +date: 2025-07-21 10:53:55 +tags: +--- + +# Web Deploy 部署任务失败解决方案 + +## 报错信息: + +```shell +C:\Program Files\dotnet\sdk\9.0.302\Sdks\Microsoft.NET.Sdk.Publish\targets\PublishTargets\Microsoft.NET.Sdk.Publish.MSDeploy.targets(140,5): 错误 : Web 部署任务失败。 +((2025/7/21 10:43:25)在远程计算机上处理请求时出错。) +(2025/7/21 10:43:25)在远程计算机上处理请求时出错。 +无法执行此操作。请与服务器管理员联系,检查授权和委派设置。 +``` + +## 解决方法: + +计算机管理 + +![image-20250721105643718](D:\source\repos\XiaodaBlogSource\source\_posts\Windows-Server\image-20250721105643718.png) + +![image-20250721105806368](D:\source\repos\XiaodaBlogSource\source\_posts\Windows-Server\image-20250721105806368.png) + +之后打开服务,找到Web 部署代理服务,重新启动一下 + +![image-20250721111012495](D:\source\repos\XiaodaBlogSource\source\_posts\Windows-Server\image-20250721111012495.png) + +# IIS(Internet Information Services) + +## 优化回收策略 + +![](https://rustfs.wenyongdalucky.club:443/hexo/image-20250806145506751.png) + +- 回收 > 固定时间间隔(分钟) + + 一个时间段,超过该时间段,应用程序池将回收。值为 0 ,则应用程序池不会按固定间隔回收 + + 默认值:1740分钟,29小时 + + 优化设置:改为0 。因为无法避免在高峰期发生回收。同时设置“回收 > 特定时间” + +- 回收 > 特定时间 + + 应用程序池进行回收的一组特定的本地时间(24小时制) + + 优化设置:固定在低峰期时回收。eg:设定为 04:00 、15:30 等 + + 另外,也可以使用windows计划任务实现iis站点每周六晚定时回收 + +- 进程模型 > 闲置超时(分钟) + + 一个时间段,设定工作进程允许保持闲置状态的最大时间间隔,超过该时间就会自动关闭。 + + 优化设置:改为0,避免内存信息频繁被回收清空。同时设置“回收 > 特定时间” + +- 进程模型 > 空闲超时操作 + + 默认是“Terminate”(另一个选项是“Suspend”)。 + + Terminate 表示一旦超时就终止服务,并回收工作进程的缓冲区的内存; + + Suspend 则悬停等待,暂不回收缓冲区内存。 + +# Windows 运行命令 + +## 进入Windows 设置 + +```cmd +ms-settings: +``` + +## 远程桌面连接 + +```cmd +mstsc +``` + +### 本地组策略编辑器 + +```cmd +gpedit.msc +``` + +## 服务 + +```cmd +services.msc +``` + +## 设备管理器 + +```cmd +devmgmt.msc +``` + +## 控制面板 + +```cmd +control +``` + +## 注册表编辑器 + +```cmd +regedit +``` + +# PowerShell + +## 激活工具命令 + +```powershell +irm https://massgrave.dev/get | iex +``` + +# CMD + +## 7zip + +### 解压压缩包内的部分文件或目录到指定目录下 + +```cmd +"C:\Users\yun\AppData\Local\Microsoft\WindowsApps\7z.exe" x "D:\source\source.7z" -o"D:" "source\repos\ruoyi-ai\*" +``` + +**详细解释:** + +- `"C:\Users\yun\AppData\Local\Microsoft\WindowsApps\7z.exe"`: 这是 7-Zip 可执行文件的完整路径。 +- `x`: 解压命令,会保留压缩包内的目录结构。 +- `"D:\source\source.7z"`: 要解压的源压缩包的完整路径。 +- `-o"D:"`: 这是指定**目标解压目录**。所有从压缩包中解压出来的内容都会放到这个目录下。 + - **注意:** `-o` 后面直接跟目标文件夹的完整路径,不带 `*`。 +- `"source\repos\ruoyi-ai\*"`: 这是**压缩包内部想要解压的目录名称**。请确保这个名称与压缩包内的实际目录名完全一致(大小写敏感)。 + +# Windows Server 2019 diff --git a/source/_posts/Windows-Server/image-20250721105643718.png b/source/_posts/Windows-Server/image-20250721105643718.png new file mode 100644 index 0000000..54690da Binary files /dev/null and b/source/_posts/Windows-Server/image-20250721105643718.png differ diff --git a/source/_posts/Windows-Server/image-20250721105806368.png b/source/_posts/Windows-Server/image-20250721105806368.png new file mode 100644 index 0000000..948e543 Binary files /dev/null and b/source/_posts/Windows-Server/image-20250721105806368.png differ diff --git a/source/_posts/Windows-Server/image-20250721111012495.png b/source/_posts/Windows-Server/image-20250721111012495.png new file mode 100644 index 0000000..8fb2233 Binary files /dev/null and b/source/_posts/Windows-Server/image-20250721111012495.png differ diff --git a/source/_posts/Windows-Terminal.md b/source/_posts/Windows-Terminal.md new file mode 100644 index 0000000..194803c --- /dev/null +++ b/source/_posts/Windows-Terminal.md @@ -0,0 +1,123 @@ +--- +title: Windows Terminal +date: 2025-12-12 09:31:56 +tags: +--- + +# SSH + +## 生成 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. 启动 **PuTTY Key Generator** + ![image-20250416151051085](https://rustfs.wenyongdalucky.club:443/hexo/image-20250416151051085.png) + +6. 选择之前生成的id_rsa + ![image-20250416151307312](https://rustfs.wenyongdalucky.club:443/hexo/image-20250416151307312.png) + +7. 出现如下,选择 **Save private key** 保存秘钥 + ![image-20250416151416275](https://rustfs.wenyongdalucky.club:443/hexo/image-20250416151416275.png) + +8. 保存到 ~/.ssh 目录即可 + ![image-20250416151503685](https://rustfs.wenyongdalucky.club:443/hexo/image-20250416151503685.png) + +## 配置 SSH + +1. 确认 .ssh 目录位置 + +SSH 的配置文件通常位于当前用户目录下的 `.ssh` 文件夹中。 + +- **路径通常是:** `C:\Users\你的用户名\.ssh\` + +你可以按 `Win + R`,输入 `%USERPROFILE%\.ssh` 并回车。 + +- **如果文件夹存在:** 直接进入。 +- **如果文件夹不存在:** 打开命令提示符(CMD)或 PowerShell,输入 `mkdir .ssh` 创建它。 + +2. 创建 config 文件 + +在 `.ssh` 文件夹内,创建一个名为 `config` 的文件。 + +- **注意:** 文件名必须是 `config`,**没有任何后缀名**(不要叫 `config.txt`)。 +- **创建方法:** 在文件夹内右键 -> 新建文本文档 -> 命名为 `config` -> 删除 `.txt` 后缀 -> 确认修改。 + +3. 编辑 config 文件 + 使用任意文本编辑器(如记事本、VS Code、Notepad++)打开该文件,按照以下格式添加内容。 + 基础语法结构 + + ``` + Host 别名 + HostName 主机IP或域名 + Port 端口号 (如果是默认22端口可省略) + User 用户名 + IdentityFile 私钥路径 (如果是密码登录可省略) + PreferredAuthentications publickey + ``` + +4. 远程 Linux 服务器配置 ssh 公钥 + ```bash + vim ~/.ssh/authorized_keys + ``` + + 内容使用**PuTTY Key Generator**,**Load Private Key** 后显示的 **Public key**,内容换行追加即可![image-20251212094845175](https://rustfs.wenyongdalucky.club:443/hexo/image-20251212094845175.png) + + + +5. 测试连接 + + ```powershell + ssh <你的Host别名> + # 例如: ssh myserver + ``` + +## 访问 Windows SSH 客户端和 SSH 服务器 + +最新版本的 Windows 10 和 Windows 11 包含基于 OpenSSH(一个使用 SSH 协议进行远程登录的连接工具)的内置 SSH 服务器和客户端。 OpenSSH 加密客户端与服务器之间的所有流量,从而遏止窃听、连接劫持和其他攻击。 + +默认情况下,OpenSSH 客户端和 OpenSSH 服务器位于以下目录:`C:\Windows\System32\OpenSSH`。 你还可以检查它是否存在于“Windows 设置”>“系统”>“可选功能”中,然后在添加的功能中搜索“OpenSSH”。 + +![ssh_optionalfeature](https://rustfs.wenyongdalucky.club:443/hexo/ssh-optionalfeature.png) + +## 创建档案 + +你可以通过执行 `ssh user@machine` 在命令提示符下启动 SSH 会话,系统将提示你输入密码。 可以将 `commandline` 设置添加到配置文件对象的 `list` 内的 [settings.json 文件](https://learn.microsoft.com/zh-cn/windows/terminal/install#settings-json-file) 中的配置文件,以创建在启动时执行此项的 Windows 终端配置文件。 + +```json +{ + "icon": "C:\\Windows\\System32\\wsl.exe", + "name": "user@machine ssh profile", + "commandline": "ssh user@machine" +} +``` + +## 指定起始目录 + +若要指定 Windows 终端调用的 ssh 会话的起始目录,可以使用以下命令: + +```json +{ + "commandline": "ssh -t bob@foo \"cd /data/bob && exec bash -l\"" +} +``` + +`-t` 标志强制执行伪终端分配。 这可用于在远程计算机上执行任意基于屏幕的程序,例如实现菜单服务。 将需要使用转义双引号,因为 bourne 外壳派生物不会为单引号中的字符串执行任何额外分析。 diff --git a/source/_posts/Winform.md b/source/_posts/Winform.md new file mode 100644 index 0000000..fcc804f --- /dev/null +++ b/source/_posts/Winform.md @@ -0,0 +1,259 @@ +--- +title: Winform +date: 2022-11-18 14:48:31 +author: 文永达 +top_img: https://gcore.jsdelivr.net/gh/volantis-x/cdn-wallpaper/abstract/B951AE18-D431-417F-B3FE-A382403FF21B.jpeg +--- + +# Winform + +## Net Framework + +### 控件属性 + +#### Name + +表示控件名 + +```c# +this.button1.Name = "button1"; +``` + +#### Text + +表示控件文本显示 + +```c# +this.button.Text = "button1"; +``` + +### 控件事件 + +#### button按钮 + +##### click + +#### comboBox + +##### SelectedIndexChanged + +问题:SelectedIndexChanged控件,初始加载的时候总会进去两次,SelectedValue 值总为System.Data.DataRowView。 + +原因:最后才发现自己是先绑定数据源,后设置控件ValueMember和DisplayMember属性。 + +解决办法:正确的做法是先设置这两个属性,后绑定数据源。 + +##### 绑定数据源 + +```c# +DataTable dt = new DataTable(); +dt.Columns.Add("ID", typeof(string)); +dt.Columns.Add("NAME", typeof(string)); + +DataRow dr = dt.NewRow(); +dr["ID"] = "1"; +dr["NAME"] = "NAME1"; + +dt.Rows.Add(dr); + +dr = dt.NewRow(); +dr["ID"] = "2"; +dr["NAME"] = "NAME2"; + +dt.Rows.Add(dr); +this.comboBox1.DisplayMember = "NAME"; +this.comboBox1.ValueMember= "ID"; +this.comboBox1.DataSource = dt; +``` + + + +### 控件文本显示国际化 + +#### 使用资源文件方式 + +在解决方案根目录新建`App_GlobalResources`文件夹 + +新建 `Resource.en-US.resx` 资源文件 + +放置英文文本 + +新建 `Resource.resx`资源文件 + +放置默认简体中文文本 + +根目录新建 `ResourceCulture.cs`类 + +```c# +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Resources; +using System.Text; +using System.Threading; + +namespace WindowsFormsApp1 +{ + public class ResourceCulture + { + // 设置需要的语言文本资源文件 + public static void SetCurrentCulture(string name) + { + if (string.IsNullOrEmpty(name)) + { + name = "en-US"; + } + Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(name); + } + // 获取资源文件中的文本 + public static string GetString(string id) + { + string strCurLanguage = ""; + try + { + ResourceManager rm = new ResourceManager("WindowsFormsApp1.App_GlobalResources.Resource", + System.Reflection.Assembly.GetExecutingAssembly()); + CultureInfo ci = Thread.CurrentThread.CurrentCulture; + + strCurLanguage = rm.GetString(id, ci); + } + catch + { + strCurLanguage = "No id" + id + ", please add."; + } + return strCurLanguage; + } + } +} + +``` + +控件里调用 + +新建initRes()方法 + +```c# +private void initRes() +{ + + // 设置 窗体form 名称 + this.Text = ResourceCulture.GetString("Form1_frmText"); + // 设置 分组框 groupbox 名称 + this.gbLanguageView.Text = ResourceCulture.GetString("gbLanguageView_frmText"); + this.gbLanguageSelection.Text = ResourceCulture.GetString("gbLanguageSelection_frmText"); +} +``` + +可以在窗体初始化调用加载 + +```c# +private void Form1_Load(object sender, EventArgs e) +{ + + this.initRes(); +} +``` + +如果是一个切换语言的窗体 + +新建可以切换的控件,这里使用`RadioButton`,因为是中英文切换,所以需要建两个`RadioButton` + +使用`click`事件 + +```c# + private void radioButton1_CheckedChanged(object sender, EventArgs e) + { + ResourceCulture.SetCurrentCulture("en-US"); + this.SetResourceCulture(); + } + +private void radioButton2_CheckedChanged(object sender, EventArgs e) +{ + ResourceCulture.SetCurrentCulture("zh-CN"); + this.SetResourceCulture(); +} +``` + +## Net Core + +### 打开其他窗体的三种方式 + +#### Show + +例如登入界面进入主页面,直接将主页面展示出来,两个窗体互不影响 + +```c# +public partial class Form1 : Form +{ + public Form1() + { + InitializeComponent(); + } + + private void button1_Click(object sender, EventArgs e) + { + Form2 form2 = new Form2(); + form2.Show(); + } +} +``` + +#### Owner + +例如text文件中的“替换”选项,打开界面后不关闭也是允许操作主页面的 + +```c# +public partial class Form1 : Form +{ + public Form1() + { + InitializeComponent(); + } + + private void button1_Click(object sender, EventArgs e) + { + Form2 form2 = new Form2(); + form2.Owner = this; + form2.Show(); + } +} +``` + +#### ShowDialog + +例如text文件中的“打开”选项,打开界面后不关闭是不允许操作主页面的 + +### 让子窗体显示在父窗体之上 + +```c# +public partial class Form1 : Form +{ + public Form1() + { + InitializeComponent(); + } + + private void button1_Click(object sender, EventArgs e) + { + Form2 form2 = new Form2(); + form2.Show(this); // this代表父窗体也就是Form1 + } +} +``` + +### TextBox + +#### 属性 + +##### Multiline + +控制编辑控件的文本是否能够跨越多行。 + +##### ScrollBars + +指示对于多行编辑控件,将为此控件显示哪些滚动条 + +##### WordWrap + +指示多行编辑控件是否自动换行 diff --git a/source/_posts/XiaodaERP.md b/source/_posts/XiaodaERP.md new file mode 100644 index 0000000..378402b --- /dev/null +++ b/source/_posts/XiaodaERP.md @@ -0,0 +1,586 @@ +--- +title: XiaodaERP +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 +--- +# XiaodaERP + +## 访问地址: [XiaodaERP](http://120.46.194.61:81/) + +## 技术选形 + +- ASP.Net 6 +- Entity Framework Core 6 +- Microsoft SQL Server + +## 开发手册 + +本节以用户模块开发为模板 + +首先数据库建表 + +### 数据库建模PDM + +#### 表名 + +![image-20221130162410150](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221130162410150.png) + +#### 表结构 + +![image-20221130162426950](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221130162426950.png) + +### SQL Server数据库建表 + +![image-20221130162700170](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221130162700170.png) + +### EF Core 建立POCO对象 + +#### 创建POCO + +POCO全称 Plain Old CLR Object 普通旧CLR对象 作为 ORM映射 + +在**解决方案资源管理器**中已创建的**Models**文件夹中新建 `User.cs`类 + +![image-20221130162955888](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221130162955888.png) + +参照数据库表结构将对应列添加进属性中 + +需要加 **? **代表可空数据类型 + +其中 **Role**是另一个POCO,在这里的作用是多表联查,用户与角色为多对一关系 + +```c# +namespace XiaodaERP.Models +{ + public class User + { + public string? UserId { get; set; } + public string? UserName { get; set; } + public string? RealName { get; set;} + public string? Avatar { get; set;} + public string? Desc { get; set;} + public string? PassWord { get; set; } + public string? HomePath { get; set; } + public Role? Role { get; set; } + public string? RoleId { get; set; } + public string? Email { get; set;} + public string? CreateUserId { get; set; } + public DateTime? CreateTime { get; set; } + public DateTime? UpdateTime{ get; set; } + public string? DeptId { get; set; } + public string? CreateUserName { get; set; } + public string? UpdateUserId { get; set; } + public string? UpdateUserName { get; set; } + } +} +``` + +#### 创建Mapping映射 + +建立ORM映射 + +在**解决方案资源管理器**中已创建的**Mapping**文件夹中新建 `UserMap.cs`类 + +![image-20221130170549473](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221130170549473.png) + +根据数据库表结构配置映射 + +```c# +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using XiaodaERP.Models; + +namespace XiaodaERP.Mapping +{ + public class UserMap : IEntityTypeConfiguration<User> + { + public void Configure(EntityTypeBuilder<User> builder) + { + // 数据库表名 + builder.ToTable("BASE_USER"); + // 数据库主键 + builder.HasKey(t => t.UserId); + // 数据库列名 + builder.Property(t => t.UserId).HasColumnName("USERID"); + builder.Property(t => t.UserName).HasColumnName("USERNAME"); + builder.Property(t => t.RealName).HasColumnName("REALNAME"); + builder.Property(t => t.Avatar).HasColumnName("AVATAR"); + builder.Property(t => t.Desc).HasColumnName("DESC"); + builder.Property(t => t.PassWord).HasColumnName("PASSWORD"); + builder.Property(t => t.HomePath).HasColumnName("HOMEPATH"); + builder.Property(t => t.CreateUserId).HasColumnName("CREATEUSERID"); + builder.Property(t => t.CreateUserName).HasColumnName("CREATEUSERNAME"); + builder.Property(t => t.UpdateUserId).HasColumnName("UPDATEUSERID"); + builder.Property(t => t.UpdateUserName).HasColumnName("UPDATEUSERNAME"); + builder.Property(t => t.CreateTime).HasColumnName("CREATETIME"); + builder.Property(t => t.UpdateTime).HasColumnName("UPDATETIME"); + builder.Property(t => t.Email).HasColumnName("EMAIL"); + builder.Property(t => t.RoleId).HasColumnName("ROLEID"); + builder.Property(t => t.DeptId).HasColumnName("DEPTID"); + // User包含一个Role 一个Role对应多个User + builder.HasOne(u => u.Role).WithMany(); + } + } +} + +``` + +#### 配置DbContext + +DbContext为EF Core数据库上下文 + +打开创建好的 `SqlServerDbContext.cs` + +加入DbSet, Mapping映射 + +```c# +using Microsoft.EntityFrameworkCore; +using XiaodaERP.Mapping; +using XiaodaERP.Models; + +namespace XiaodaERP +{ + public class SqlServerDbContext : DbContext + { + public SqlServerDbContext(DbContextOptions<SqlServerDbContext> options) + : base(options) + { + } + // DbSet + public DbSet<User> Users { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + // Mapping映射 + modelBuilder.ApplyConfiguration<User>(new UserMap()); + + base.OnModelCreating(modelBuilder); + } + + } +} + +``` + +#### 创建VO + +VO全称ViewObject 视图对象 作为 API与前端数据交换的媒介 + +在**解决方案资源管理器**中已创建的**Models**文件夹中新建 `ViewUser.cs`类 + +```c# +using System.Security.Permissions; + +namespace XiaodaERP.Models +{ + public class ViewUser + { + public string? UserId { get; set; } + public string? username { get; set; } + public string? RealName { get; set; } + public string? Avatar { get; set; } + public string? Desc { get; set; } + public string? password { get; set; } + public string? Token { get; set; } + public string? HomePath { get; set; } + public string? RoleId { get; set; } + public string? RoleName { get; set; } + public string? Email { get; set; } + public string? CreateUser { get; set; } + public string? DeptId { get; set; } + public Role[]? Roles { get; set; } + public int? Total { get; set; } + } +} + +``` + +#### 创建 `Service` + +在**解决方案资源管理器**中已创建的**Services**文件夹的**IServices**文件夹中新建 `IAccountService.cs`接口 + +![image-20221207091417161](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221207091417161.png) + +```c# +using XiaodaERP.Models; +using XiaodaERP.Utils; + +namespace XiaodaERP.Services.IServices +{ + public interface IAccountService + { + // 查询所有Account 条件查询 + List<ViewUser> GetAllAccounts(string DeptId, string? userName, string? realName); + // 添加与更新Account + bool UpdateAccount(ViewUser viewUser); + // 删除Account根据主键ID + bool DeleteAccount(string UserId); + + bool ResetToDefaultPassword(string UserId); + // 分页条件查询Account + PageResult<ViewUser> GetAllAccountsPagination(int page, int pageSize, string DeptId, string? userName, string? realName); + } +} +``` + +在**Services**文件夹中新建 `AccountService.cs`类,并实现 `IAccountService.cs`接口 + +```c# +using AutoMapper; +using Castle.Core.Internal; +using Microsoft.EntityFrameworkCore; +using Microsoft.OpenApi.Validations; +using Newtonsoft.Json.Linq; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using XiaodaERP.Models; +using XiaodaERP.Services.IServices; +using XiaodaERP.Utils; + +namespace XiaodaERP.Services +{ + public class AccountService : IAccountService + { + private readonly OracleDbContext _oracleDbContext; + private readonly SqlServerDbContext _sqlServerDbContext; + private readonly TokenHelper _tokenHelper; + public AccountService(OracleDbContext oracleDbContext, SqlServerDbContext sqlServerDbContext, TokenHelper tokenHelper) + { + _oracleDbContext = oracleDbContext; + _sqlServerDbContext = sqlServerDbContext; + _tokenHelper = tokenHelper; + } + + public bool DeleteAccount(string UserId) + { + var res = _sqlServerDbContext.Users.Where(t => t.UserId == UserId).FirstOrDefault(); + if (res != null) + { + _sqlServerDbContext.Users.Remove(res); + return _sqlServerDbContext.SaveChanges() > 0; + } + else + { + return false; + } + } + + public List<ViewUser> GetAllAccounts(string DeptId, string? userName, string? realName) + { + var res = new List<User>(); + if (!string.IsNullOrEmpty(DeptId)) + { + var pdeptRes = _sqlServerDbContext.Depts + .Where(t => t.Id == DeptId).Select(t => t.ParentDept).FirstOrDefault(); + // 如果pdept是空的,是一级部门 + if (string.IsNullOrEmpty(pdeptRes)) + { + if (!userName.IsNullOrEmpty() && realName.IsNullOrEmpty()) + { + res = _sqlServerDbContext.Users.Include(user => user.Role) + .Where(u => (_sqlServerDbContext.Depts.Where(d => d.ParentDept == DeptId) + .Select(d => d.Id) + .ToList()) + .Contains(u.DeptId)) + .Where(u => EF.Functions.Like(u.UserName, "%" + userName + "%")).ToList(); + } + if (!realName.IsNullOrEmpty() && userName.IsNullOrEmpty()) + { + res = _sqlServerDbContext.Users.Include(user => user.Role) + .Where(u => (_sqlServerDbContext.Depts.Where(d => d.ParentDept == DeptId) + .Select(d => d.Id) + .ToList()) + .Contains(u.DeptId)) + .Where(u => EF.Functions.Like(u.RealName, "%" + realName + "%")).ToList(); + } + if (!userName.IsNullOrEmpty() && !realName.IsNullOrEmpty()) + { + res = _sqlServerDbContext.Users.Include(user => user.Role) + .Where(u => (_sqlServerDbContext.Depts.Where(d => d.ParentDept == DeptId) + .Select(d => d.Id) + .ToList()) + .Contains(u.DeptId)) + .Where(u => EF.Functions.Like(u.UserName, "%" + userName + "%")) + .Where(u => EF.Functions.Like(u.RealName, "%" + realName + "%")).ToList(); + } + if (userName.IsNullOrEmpty() && realName.IsNullOrEmpty()) + { + res = _sqlServerDbContext.Users.Include(user => user.Role) + .Where(u => (_sqlServerDbContext.Depts.Where(d => d.ParentDept == DeptId) + .Select(d => d.Id) + .ToList()) + .Contains(u.DeptId)).ToList(); + } + } + else + { + res = _sqlServerDbContext.Users + .Include(user => user.Role) + .Where(u => u.DeptId == DeptId).ToList(); + } + } + else + { + res = _sqlServerDbContext.Users.Include(user => user.Role).ToList(); + } + + List<ViewUser> viewUsers = new(); + var config = new MapperConfiguration(cfg => cfg.CreateMap<User, ViewUser>() + .ForMember(dest => dest.username, opt => opt.MapFrom(src => src.UserName)) + .ForMember(dest => dest.RoleId, opt => opt.MapFrom(src => src.Role.Id)) + .ForMember(dest => dest.RoleName, opt => opt.MapFrom(src => src.Role.RoleName))); + var mapper = config.CreateMapper(); + foreach (var user in res) + { + viewUsers.Add( + mapper.Map<ViewUser>(user) + ) ; + } + return viewUsers; + } + + public bool UpdateAccount(ViewUser viewUser) + { + var res = _sqlServerDbContext.Users.FirstOrDefault(x => x.UserId == viewUser.UserId); + var config = new MapperConfiguration(cfg => cfg.CreateMap<ViewUser, User>() + .ForMember(dest => dest.UserName, opt => opt.MapFrom(src => src.username)) + .BeforeMap((src, des) => src.UserId = Guid.NewGuid().ToString().Replace("-", "").ToUpper()) + .BeforeMap((src, des) => src.password = this.Md5Encoding("123456")) + .BeforeMap((src, des) => des.CreateTime = DateTime.Now) + .BeforeMap((src, des) => des.CreateUserId = _tokenHelper.GetToken<ViewUser>(TokenHelper.Token).UserId) + .BeforeMap((src, des) => des.CreateUserName = _tokenHelper.GetToken<ViewUser>(TokenHelper.Token).username)); + var mapper = config.CreateMapper(); + if (res == null) + { + _sqlServerDbContext.Users.Add( + mapper.Map<User>(viewUser) + ); + } + else + { + res = new MapperConfiguration(cfg => cfg.CreateMap<ViewUser, User>() + .BeforeMap((src, des) => src.password = des.PassWord) + .BeforeMap((src, des) => des.UpdateTime = DateTime.Now) + .BeforeMap((src, des) => des.UpdateUserId = _tokenHelper.GetToken<ViewUser>(TokenHelper.Token).UserId) + .BeforeMap((src, des) => des.UpdateUserName = _tokenHelper.GetToken<ViewUser>(TokenHelper.Token).username)) + .CreateMapper().Map(viewUser, res); + } + return _sqlServerDbContext.SaveChanges() > 0; + } + + /// <summary> + /// MD5 加密字符串 + /// </summary> + /// <param name="rawPass">源字符串</param> + /// <returns>加密后字符串</returns> + public string Md5Encoding(string rawPass) + { + // 创建MD5类的默认实例:MD5CryptoServiceProvider + var md5 = MD5.Create(); + var bs = Encoding.UTF8.GetBytes(rawPass); + var hs = md5.ComputeHash(bs); + var sb = new StringBuilder(); + foreach (var b in hs) + { + // 以十六进制格式格式化 + sb.Append(b.ToString("x2")); + } + return sb.ToString(); + } + + public bool ResetToDefaultPassword(string UserId) + { + var res = _sqlServerDbContext.Users.Where(t => t.UserId == UserId).FirstOrDefault(); + if (res != null) + { + res.UpdateTime = DateTime.Now; + res.UpdateUserId = _tokenHelper.GetToken<ViewUser>(TokenHelper.Token).UserId; + res.UpdateUserName = _tokenHelper.GetToken<ViewUser>(TokenHelper.Token).username; + res.PassWord = Md5Encoding("123456"); + return _sqlServerDbContext.SaveChanges() > 0; + } + else + { + return false; + } + } + + public PageResult<ViewUser> GetAllAccountsPagination(int page, int pageSize, string DeptId, string? userName, string? realName) + { + var res = new List<User>(); + int count = 0; + if (!string.IsNullOrEmpty(DeptId)) + { + var pdeptRes = _sqlServerDbContext.Depts + .Where(t => t.Id == DeptId).Select(t => t.ParentDept).FirstOrDefault(); + // 如果pdept是空的,是一级部门 一级部门查询 + if (string.IsNullOrEmpty(pdeptRes)) + { + var sql = _sqlServerDbContext.Users.Include(user => user.Role) + .Where(u => (_sqlServerDbContext.Depts.Where(d => d.ParentDept == DeptId) + .Select(d => d.Id) + .ToList()) + .Contains(u.DeptId)) + .AsQueryable(); + if (!userName.IsNullOrEmpty() && realName.IsNullOrEmpty()) + { + res = sql + .Where(u => EF.Functions.Like(u.UserName, "%" + userName + "%")) + .Skip((page - 1) * pageSize).Take(pageSize).ToList(); + count = sql.Where(u => EF.Functions.Like(u.UserName, "%" + userName + "%")).Count(); + } + if (!realName.IsNullOrEmpty() && userName.IsNullOrEmpty()) + { + res = sql + .Where(u => EF.Functions.Like(u.RealName, "%" + realName + "%")) + .Skip((page - 1) * pageSize).Take(pageSize).ToList(); + count = sql.Where(u => EF.Functions.Like(u.RealName, "%" + realName + "%")).Count(); + } + if (!userName.IsNullOrEmpty() && !realName.IsNullOrEmpty()) + { + res = sql + .Where(u => EF.Functions.Like(u.UserName, "%" + userName + "%")) + .Where(u => EF.Functions.Like(u.RealName, "%" + realName + "%")) + .Skip((page - 1) * pageSize).Take(pageSize).ToList(); + count = sql.Where(u => EF.Functions.Like(u.UserName, "%" + userName + "%")) + .Where(u => EF.Functions.Like(u.RealName, "%" + realName + "%")).Count(); + } + if (userName.IsNullOrEmpty() && realName.IsNullOrEmpty()) + { + res = sql.Skip((page - 1) * pageSize).Take(pageSize).ToList(); + count = sql.Count(); + } + } // 二级部门查询 + else + { + var sql = _sqlServerDbContext.Users + .Include(user => user.Role) + .Where(u => u.DeptId == DeptId).AsQueryable(); + if (!userName.IsNullOrEmpty() && realName.IsNullOrEmpty()) + { + res = sql + .Where(u => EF.Functions.Like(u.UserName, "%" + userName + "%")) + .Skip((page - 1) * pageSize).Take(pageSize).ToList(); + count = sql.Where(u => EF.Functions.Like(u.UserName, "%" + userName + "%")).Count(); + } + if (!realName.IsNullOrEmpty() && userName.IsNullOrEmpty()) + { + res = sql + .Where(u => EF.Functions.Like(u.RealName, "%" + realName + "%")) + .Skip((page - 1) * pageSize).Take(pageSize).ToList(); + count = sql.Where(u => EF.Functions.Like(u.RealName, "%" + realName + "%")).Count(); + } + if (!userName.IsNullOrEmpty() && !realName.IsNullOrEmpty()) + { + res = sql + .Where(u => EF.Functions.Like(u.UserName, "%" + userName + "%")) + .Where(u => EF.Functions.Like(u.RealName, "%" + realName + "%")) + .Skip((page - 1) * pageSize).Take(pageSize).ToList(); + count = sql.Where(u => EF.Functions.Like(u.UserName, "%" + userName + "%")) + .Where(u => EF.Functions.Like(u.RealName, "%" + realName + "%")).Count(); + } + if (userName.IsNullOrEmpty() && realName.IsNullOrEmpty()) + { + res = sql.ToList(); + count = sql.Count(); + } + } + } + else + { + res = _sqlServerDbContext.Users.Include(user => user.Role).ToList(); + count = _sqlServerDbContext.Users.Include(user => user.Role).Count(); + } + + List<ViewUser> viewUsers = new(); + var config = new MapperConfiguration(cfg => cfg.CreateMap<User, ViewUser>() + .ForMember(dest => dest.username, opt => opt.MapFrom(src => src.UserName)) + .ForMember(dest => dest.RoleId, opt => opt.MapFrom(src => src.Role.Id)) + .ForMember(dest => dest.RoleName, opt => opt.MapFrom(src => src.Role.RoleName)) + .AfterMap((src, des) => des.password = null)); + var mapper = config.CreateMapper(); + foreach (var user in res) + { + viewUsers.Add( + mapper.Map<ViewUser>(user) + ); + } + return new PageResult<ViewUser> + { + Items = viewUsers, + Total = count + }; + } + } +} +``` + +#### 创建 `Controller` + +Controller即控制器,API,负责后端与前端数据交互 + +在**解决方案资源管理器**中已创建的**Controllers**文件夹中新建 `AccountController.cs`类 + +![image-20221207091644868](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221207091644868.png) + +遵循RestFul风格 查询使用Get请求,增加与更新使用Post请求,删除使用Delete请求 + +```c# +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using XiaodaERP.Attributes; +using XiaodaERP.Models; +using XiaodaERP.Services; +using XiaodaERP.Services.IServices; +using XiaodaERP.Utils; + +namespace XiaodaERP.Controllers +{ + // 使用WebAPI + [ApiController] + [Route("/api/[controller]/[action]")] // 规定API URL格式 + public class AccountController : ControllerBase + { + private readonly IAccountService _accountService; + // 注入AccountService + public AccountController (IAccountService accountService) + { + _accountService = accountService; + } + // 添加与更新Account Post请求 Name指定 action 的Url + [Authorize] + [HttpPost(Name = "postAccount")] + public ResultUtil PostAccount(ViewUser viewUser) => + ResultUtil.ok(_accountService.UpdateAccount(viewUser)); + + //[AuthFilter] + [TypeFilter(typeof(AuthFilter))] // 自定义AOP注解 记录接口操作日志 + [Authorize] // 需要携带Token认证的接口 + [HttpGet(Name = "getAllAccountList")] + public ResultUtil GetAllAccountList(int? page, int? pageSize, string? deptId, string? userName, string? realName) + { + if (page != null && pageSize != null) + { + return ResultUtil.ok(_accountService.GetAllAccountsPagination((int) page, (int) pageSize, deptId, userName, realName)); + } + else + { + return ResultUtil.ok(_accountService.GetAllAccounts(deptId, userName, realName)); + } + + } + + [Authorize] + [HttpPut(Name = "resetToDefaultPassword")] + public ResultUtil ResetToDefaultPassword(string userId) + => ResultUtil.ok(_accountService.ResetToDefaultPassword(userId)); + + [Authorize] + [HttpDelete(Name = "deleteAccount")] + public ResultUtil DeleteAccount(string userId) => ResultUtil.ok(_accountService.DeleteAccount(userId)); + } +} + +``` diff --git a/source/_posts/curl.md b/source/_posts/curl.md new file mode 100644 index 0000000..8107559 --- /dev/null +++ b/source/_posts/curl.md @@ -0,0 +1,476 @@ +--- +title: curl 命令完全指南 +date: 2026-03-27 10:10:35 +tags: + - curl + - Linux + - Windows + - API测试 +--- + +## 简介 + +curl (Client for URLs) 是一个强大的命令行工具,用于传输数据。它支持多种协议(HTTP、HTTPS、FTP等),是开发者和运维人员必备的工具之一。 + +## 安装 + +### Linux + +大多数 Linux 发行版已预装 curl: + +```bash +# Debian/Ubuntu +sudo apt install curl + +# CentOS/RHEL +sudo yum install curl + +# Arch Linux +sudo pacman -S curl + +# 验证安装 +curl --version +``` + +### Windows + +**方式一:Windows 10/11 自带** +Windows 10 1803+ 和 Windows 11 已内置 curl。 + +**方式二:手动安装** +1. 访问 https://curl.se/windows/ 下载 +2. 解压到 `C:\tools\curl` +3. 添加到系统 PATH 环境变量 + +**方式三:使用包管理器** +```powershell +# winget +winget install curl + +# Chocolatey +choco install curl + +# Scoop +scoop install curl +``` + +## 基础用法 + +### GET 请求 + +```bash +# 基本 GET 请求 +curl https://api.example.com/users + +# 带查询参数 +curl "https://api.example.com/users?page=1&limit=10" + +# 只显示响应头 +curl -I https://api.example.com + +# 显示响应头和响应体 +curl -i https://api.example.com + +# 跟随重定向 +curl -L https://example.com/redirect +``` + +### POST 请求 + +```bash +# 发送 JSON 数据(请求体) +curl -X POST https://api.example.com/users \ + -H "Content-Type: application/json" \ + -d '{"name": "张三", "email": "zhangsan@example.com"}' + +# 发送表单数据 +curl -X POST https://api.example.com/login \ + -d "username=admin&password=123456" + +# 发送文件 +curl -X POST https://api.example.com/upload \ + -F "file=@/path/to/file.jpg" + +# 发送多部分表单(文件+字段) +curl -X POST https://api.example.com/upload \ + -F "file=@/path/to/file.jpg" \ + -F "name=张三" \ + -F "description=用户头像" +``` + +### PUT / PATCH / DELETE 请求 + +```bash +# PUT 请求(完整更新) +curl -X PUT https://api.example.com/users/1 \ + -H "Content-Type: application/json" \ + -d '{"name": "李四", "email": "lisi@example.com"}' + +# PATCH 请求(部分更新) +curl -X PATCH https://api.example.com/users/1 \ + -H "Content-Type: application/json" \ + -d '{"name": "李四"}' + +# DELETE 请求 +curl -X DELETE https://api.example.com/users/1 +``` + +## 常用选项详解 + +| 选项 | 说明 | 示例 | +|------|------|------| +| `-X` | 指定请求方法 | `-X POST` | +| `-H` | 添加请求头 | `-H "Authorization: Bearer token"` | +| `-d` | 发送数据 | `-d '{"key":"value"}'` | +| `-F` | 上传文件/表单 | `-F "file=@photo.jpg"` | +| `-u` | 基础认证 | `-u user:pass` | +| `-I` | 只获取响应头 | `curl -I url` | +| `-i` | 显示响应头和内容 | `curl -i url` | +| `-L` | 跟随重定向 | `curl -L url` | +| `-o` | 保存到文件 | `-o output.json` | +| `-O` | 使用远程文件名保存 | `-O` | +| `-s` | 静默模式 | `-s` | +| `-v` | 显示详细信息 | `-v` | +| `-k` | 忽略 SSL 证书验证 | `-k` | +| `-x` | 使用代理 | `-x http://proxy:8080` | +| `--connect-timeout` | 连接超时(秒) | `--connect-timeout 10` | +| `-m` | 最大请求时间(秒) | `-m 30` | + +## 替代 Postman 的完整方案 + +### 1. 认证请求 + +```bash +# Bearer Token 认证 +curl -H "Authorization: Bearer your_token_here" \ + https://api.example.com/data + +# Basic Auth 认证 +curl -u "username:password" \ + https://api.example.com/data + +# API Key 认证(Header 方式) +curl -H "X-API-Key: your_api_key" \ + https://api.example.com/data + +# API Key 认证(Query 参数方式) +curl "https://api.example.com/data?api_key=your_api_key" +``` + +### 2. 处理 Cookie + +```bash +# 保存 Cookie 到文件 +curl -c cookies.txt https://api.example.com/login \ + -d "username=admin&password=123456" + +# 使用已保存的 Cookie +curl -b cookies.txt https://api.example.com/profile + +# 同时读写 Cookie +curl -b cookies.txt -c cookies.txt https://api.example.com/action + +# 直接设置 Cookie +curl -H "Cookie: session=abc123; token=xyz789" \ + https://api.example.com/data +``` + +### 3. 美化 JSON 输出 + +```bash +# 使用 jq 美化(Linux/macOS) +curl -s https://api.example.com/users | jq + +# 使用 Python 美化(跨平台) +curl -s https://api.example.com/users | python -m json.tool + +# Windows PowerShell 方式 +curl -s https://api.example.com/users | ConvertFrom-Json | ConvertTo-Json -Depth 10 +``` + +### 4. 保存请求集合 + +创建一个 `api-requests.sh` 文件: + +```bash +#!/bin/bash +# API 请求集合 - 类似 Postman Collection + +BASE_URL="https://api.example.com" +TOKEN="your_token_here" + +# 获取用户列表 +get_users() { + curl -s "$BASE_URL/users" \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" +} + +# 创建用户 +create_user() { + curl -s -X POST "$BASE_URL/users" \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"name": "张三", "email": "zhangsan@example.com"}' +} + +# 获取单个用户 +get_user() { + local id=$1 + curl -s "$BASE_URL/users/$id" \ + -H "Authorization: Bearer $TOKEN" +} + +# 调用示例 +case "$1" in + users) get_users | jq ;; + create) create_user | jq ;; + user) get_user "$2" | jq ;; + *) echo "用法: $0 {users|create|user <id>}" ;; +esac +``` + +使用方式: +```bash +chmod +x api-requests.sh +./api-requests.sh users +./api-requests.sh create +./api-requests.sh user 1 +``` + +### 5. 环境变量配置 + +```bash +# 创建 .env 文件(添加到 .gitignore) +# .env +API_BASE_URL=https://api.example.com +API_TOKEN=your_token_here + +# 在脚本中使用 +source .env + +curl -H "Authorization: Bearer $API_TOKEN" \ + "$API_BASE_URL/users" +``` + +### 6. 环境切换 + +创建多环境配置: + +```bash +# environments.sh +get_env() { + case "$1" in + dev) + BASE_URL="https://dev-api.example.com" + TOKEN="$DEV_TOKEN" + ;; + staging) + BASE_URL="https://staging-api.example.com" + TOKEN="$STAGING_TOKEN" + ;; + prod) + BASE_URL="https://api.example.com" + TOKEN="$PROD_TOKEN" + ;; + *) + echo "未知环境: $1" + exit 1 + ;; + esac +} + +# 使用 +source environments.sh +get_env dev +curl -H "Authorization: Bearer $TOKEN" "$BASE_URL/users" +``` + +### 7. 测试脚本示例 + +```bash +#!/bin/bash +# API 测试脚本 + +BASE_URL="https://api.example.com" +TOKEN="test_token" + +echo "=== 测试用户 API ===" + +# 测试获取用户 +echo "1. GET /users" +response=$(curl -s -w "\n%{http_code}" "$BASE_URL/users" \ + -H "Authorization: Bearer $TOKEN") +http_code=$(echo "$response" | tail -n1) +body=$(echo "$response" | head -n -1) + +if [ "$http_code" -eq 200 ]; then + echo "✅ 成功 (HTTP $http_code)" + echo "$body" | jq +else + echo "❌ 失败 (HTTP $http_code)" + echo "$body" +fi + +# 测试创建用户 +echo -e "\n2. POST /users" +response=$(curl -s -w "\n%{http_code}" -X POST "$BASE_URL/users" \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"name": "测试用户"}') +http_code=$(echo "$response" | tail -n1) +body=$(echo "$response" | head -n -1) + +if [ "$http_code" -eq 201 ]; then + echo "✅ 成功 (HTTP $http_code)" +else + echo "❌ 失败 (HTTP $http_code)" +fi +``` + +## Windows 特殊处理 + +### PowerShell 中的引号问题 + +PowerShell 对单引号和双引号有特殊处理,推荐以下方式: + +```powershell +# 方式一:使用双引号,内部双引号用反引号转义 +curl -X POST https://api.example.com/users ` + -H "Content-Type: application/json" ` + -d "{`"name`": `"张三`"}" + +# 方式二:使用单引号包裹 JSON(推荐) +curl -X POST https://api.example.com/users ` + -H "Content-Type: application/json" ` + -d '{"name": "张三"}' + +# 方式三:从文件读取数据 +curl -X POST https://api.example.com/users ` + -H "Content-Type: application/json" ` + -d "@request.json" +``` + +### Windows CMD 中的处理 + +```cmd +# 使用双引号,内部双引号用反斜杠转义 +curl -X POST https://api.example.com/users ^ + -H "Content-Type: application/json" ^ + -d "{\"name\": \"张三\"}" + +# 或使用文件 +curl -X POST https://api.example.com/users ^ + -H "Content-Type: application/json" ^ + -d "@request.json" +``` + +### 创建 request.json 文件 + +对于复杂的请求体,推荐使用文件: + +```json +{ + "name": "张三", + "email": "zhangsan@example.com", + "roles": ["admin", "user"], + "settings": { + "theme": "dark", + "language": "zh-CN" + } +} +``` + +```bash +curl -X POST https://api.example.com/users \ + -H "Content-Type: application/json" \ + -d @request.json +``` + +## 实用技巧 + +### 1. 测量请求时间 + +```bash +curl -w "DNS: %{time_namelookup}s\nConnect: %{time_connect}s\nTTFB: %{time_starttransfer}s\nTotal: %{time_total}s\n" \ + -o /dev/null -s https://api.example.com +``` + +### 2. 下载文件并显示进度 + +```bash +curl -O --progress-bar https://example.com/large-file.zip +``` + +### 3. 断点续传 + +```bash +curl -C - -O https://example.com/large-file.zip +``` + +### 4. 限速下载 + +```bash +# 限制为 1MB/s +curl --limit-rate 1M -O https://example.com/file.zip +``` + +### 5. 批量下载 + +```bash +# 下载多个文件 +curl -O https://example.com/file1.zip -O https://example.com/file2.zip + +# 使用序列 +curl -O https://example.com/file[1-10].jpg + +# 使用字母序列 +curl -O https://example.com/file[a-z].jpg +``` + +### 6. 调试请求 + +```bash +# 显示完整请求/响应信息 +curl -v https://api.example.com + +# 输出详细信息到文件 +curl --trace-ascii debug.txt https://api.example.com +``` + +## curl vs Postman 对比 + +| 功能 | curl | Postman | +|------|------|---------| +| 安装 | 系统内置/轻量 | 需下载安装 | +| 脚本化 | ✅ 原生支持 | ❌ 需导出 | +| 自动化 | ✅ 易于集成 CI/CD | 需 Newman | +| 资源占用 | 极低 | 较高 | +| 可视化 | ❌ 无 | ✅ 有 | +| 学习曲线 | 较陡 | 平缓 | +| 环境管理 | 手动配置 | 图形界面 | +| 团队协作 | Git + 脚本 | 云同步 | +| 价格 | 免费 | 免费版有限制 | + +## 快速参考 + +```bash +# 最常用的命令模板 +curl -X [METHOD] [URL] \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -d '[JSON_DATA]' | jq + +# 常用组合 +curl -s -X POST $URL -H $HEADERS -d $DATA | jq # 静默 POST,美化输出 +curl -I $URL # 检查响应头 +curl -L -I $URL # 跟随重定向并显示头 +curl -v $URL # 调试模式 +curl -u user:pass $URL # Basic Auth +``` + +## 总结 + +curl 是一个功能强大的 HTTP 客户端工具,通过脚本化和命令行参数可以完全替代 Postman 进行 API 测试。虽然缺少可视化界面,但其轻量、可自动化、易于集成的特点使其成为开发者和 DevOps 的首选工具。 + +掌握 curl 能让你在没有图形界面的服务器环境中也能灵活地进行 API 调试和测试,是每个开发者必备的技能。 \ No newline at end of file diff --git a/source/_posts/gRPC.md b/source/_posts/gRPC.md new file mode 100644 index 0000000..e0633b2 --- /dev/null +++ b/source/_posts/gRPC.md @@ -0,0 +1,311 @@ +--- +title: gRPC入门与实操 +date: 2023-01-16 12:37:31 +author: 文永达 +top_img: https://gcore.jsdelivr.net/gh/volantis-x/cdn-wallpaper/abstract/67239FBB-E15D-4F4F-8EE8-0F1C9F3C4E7C.jpeg +--- + +# gRPC入门与实操 + +## 为什么选择gRPC + +### 历史 + +长久以来,我们在前后端交互时使用`WebApi + JSON`方式,后端服务之间调用同样如此(或者更久远之前的`WCF + XML`方式)。WebApi + JSON 是优选的,很重要的一点是它们两者都是平台无关的三方标准,且足够语义化,便于程序员使用,在异构(前后端、多语言后端)交互场景下是不二选择。然而,在后端服务体系改进特别是后来微服务兴起后,我们发现,前后端交互理所当然认可的 WebApi + JSON 在后端体系内显得有点不太合适: + +1. JSON 字符编码方式使得传输数据量较大,而后端一般并不需要直接操作 JSON,都会将 JSON 转为平台专有类型后再处理;既然需要转换,为什么不选择一个数据量更小,转换更方便的格式呢? +2. 调用双方要事先约定数据结构和调用接口,稍有变动就要手动更新相关代码(Model 类和方法签名);是否可以将约定固化为文档,服务提供者维护该文档,调用方根据该文档可以方便地生成自己需要的代码,在文档变化时代码也可以自动更新? +3. [之前] WebApi 基于的 Http[1.1] 协议已经诞生 20 多年,其定义的交互模式在今日已经捉襟见肘;业界需要一个更有效率的协议。 + +### 高效传输-Http2.0 + +我们先来说第 3 个问题,其实很多大厂内部早已开始着手处理,并诞生了一些应用广泛的框架,如阿里开源的`Dubbo`,直接抛弃了 Http 改为基于 TCP实现,效率得到明显提升,不过 Dubbo 依赖 Java 环境,无法跨平台使用,不在我们考虑范围。 + +另一个大厂 Google,内部也在长期使用自研的`Stubby`框架,与 Dubbo 不同的是,Stubby是跨平台的,但是 Google 认为 Stubby不基于任何标准,而且与其内部基础设施紧密耦合,并不适合公开发布。 + +同时 Google 也在对 Http1.1 协议进行增强,该项目是 2012 年提出的 SPDY 方案,其优化了 Http 协议层,新增的功能包括数据流的多路复用、请求优先级以及HTTP报头压缩。Google 表示,引入 SPDY 协议后,在实验室测试中页面加载速度比原先快 64%。巨大的提升让大家开始从正面看待和解决老版本 Http 协议的问题,这也直接加速了 Http2.0 的诞生。实际上,Http2.0 是以 SPDY 为原型进行讨论和标准化的,当然也做了更多的改进和调整。 + +随着 Http2.0 的出现和普及,许多与 Stubby 相同的功能已经出现在公共标准中,包括 Stubby 未提供的其他功能。很明显,是时候重做 Stubby 以利用这种标准化,并将其适用范围扩展到分布式计算的最后一英里,支持移动设备(如安卓)、物联网(IOT)、和浏览器连接到后端服务。 + +2015 年 3 月,Google决定在公开场合构建下一版 Stubby,以便与业界分享经验,并进行相关合作,也就是本文的主角`gRPC`。 + +### 高效编码-protobuf + +回头来看第 1 个问题,解决起来相对比较简单,无非是将傻瓜式字符编码转为更有效的二进制编码(比如数字 10000 JSON 编码后是 5 个字节,按整型编码就是 4 个字节),同时加上些事先约定的编码算法使得最终结果更紧凑。常见的平台无关的编码格式有`MessagePack`和`protobuf`等,我们以 protobuf 为例。 + +protobuf 采用 `varint` 和 处理负数的 `ZigZag` 两种编码方式使得数值字段占用空间大大减少;同时它约定了字段类型和标识,采用 `TLV` 方式,将字段名映射为小范围结果集中的一项(比如对于不超过 256 个字段的数据体来说,不管字段名本身的长度多少,每个字段名都只要 1 个字节就能标识),同时移除了分隔符,并且可以过滤空字段(若字段没有被赋值,那么该字段不会出现在序列化结果中)。 + +### 高效编程-代码生成工具 + +第 2 个问题呢,其实需要的就是[每个平台]一套代码生成工具。生成的代码需要覆盖类的定义、对象的序列化/反序列化、服务接口的暴露和远程调用等等必要的模板代码,如此,开发人员只需要负责接口文档的维护和业务代码的实现(很自然的面向接口编程:))。此时,采用 protobuf 的`gRPC`自然而然的映入眼帘,因为对于目前所有主要的编程语言和平台,都有 gRPC 工具和库,包括 .NET、Java、Python、Go、C++、Node.js、Swift、Dart、Ruby 以及 PHP。可以说,这些工具和库的提供,使得 gRPC 可以跨多种语言和平台一致地工作,成为一个全面的 RPC 解决方案。 + +## proto文件 + +```protobuf +syntax = "proto3"; + +option csharp_namespace = "GrpcDemo.Service"; + +package greet; + +// The greeting service definition. +service Greeter { + // Sends a greeting + rpc SayHello (HelloRequest) returns (HelloReply); +} + +// The request message containing the user's name. +message HelloRequest { + string name = 1; +} + +// The response message containing the greetings. +message HelloReply { + string message = 1; +} + +``` + +- syntax 标识Protobuf版本为v3 +- option csharp_namespace 标识生成C#类的命名空间 +- package 标识proto文件的命名空间 +- service 定义服务 +- rpc FuncName (Input) returns (Output) 定义一个远程过程 +- message 声明数据结构 + +### Protobuf 消息(message) + +消息是 Protobuf 中的主要数据传输对象。 它们在概念上类似于 .NET 类。 + +```protobuf +syntax = "proto3"; + +option csharp_namespace = "Contoso.Messages"; + +message Person { + int32 id = 1; + string first_name = 2; + string last_name = 3; +} +``` + +前面的消息定义将三个字段指定为名称/值对。 与 .NET 类型上的属性类似,每个字段都有名称和类型。 字段类型可以是 Protobuf 标量值类型(如 `int32`),也可以是其他消息。 + +### 标量值类型 + +Protobuf 支持一系列本机标量值类型。 下表列出了全部本机标量值类型及其等效 C# 类型: + +| **Protobuf 类型** | **C# 类型** | +| ----------------- | ------------ | +| `double` | `double` | +| `float` | `float` | +| `int32` | `int` | +| `int64` | `long` | +| `uint32` | `uint` | +| `uint64` | `ulong` | +| `sint32` | `int` | +| `sint64` | `long` | +| `fixed32` | `uint` | +| `fixed64` | `ulong` | +| `sfixed32` | `int` | +| `sfixed64` | `long` | +| `bool` | `bool` | +| `string` | `string` | +| `bytes` | `ByteString` | + +### 日期和时间 + +本机标量类型不提供与 .NET 的 [DateTimeOffset](https://learn.microsoft.com/zh-cn/dotnet/api/system.datetimeoffset)、[DateTime](https://learn.microsoft.com/zh-cn/dotnet/api/system.datetime) 和 [TimeSpan](https://learn.microsoft.com/zh-cn/dotnet/api/system.timespan) 等效的日期和时间值。 可使用 Protobuf 的一些“已知类型”扩展来指定这些类型。 这些扩展为受支持平台中的复杂字段类型提供代码生成和运行时支持。 + +下表显示日期和时间类型: + +| .NET 类型 | Protobuf 已知类型 | +| :--------------- | :-------------------------- | +| `DateTimeOffset` | `google.protobuf.Timestamp` | +| `DateTime` | `google.protobuf.Timestamp` | +| `TimeSpan` | `google.protobuf.Duration` | + +```c# +syntax = "proto3"; + +import "google/protobuf/duration.proto"; +import "google/protobuf/timestamp.proto"; + +message Meeting { + string subject = 1; + google.protobuf.Timestamp start = 2; + google.protobuf.Duration duration = 3; +} +``` + +C# 类中生成的属性不是 .NET 日期和时间类型。 属性使用 `Google.Protobuf.WellKnownTypes` 命名空间中的 `Timestamp` 和 `Duration` 类。 这些类提供在 `DateTimeOffset`、`DateTime` 和 `TimeSpan` 之间进行转换的方法。 + +```c# +// Create Timestamp and Duration from .NET DateTimeOffset and TimeSpan. +var meeting = new Meeting +{ + Time = Timestamp.FromDateTimeOffset(meetingTime), // also FromDateTime() + Duration = Duration.FromTimeSpan(meetingLength) +}; + +// Convert Timestamp and Duration to .NET DateTimeOffset and TimeSpan. +var time = meeting.Time.ToDateTimeOffset(); +var duration = meeting.Duration?.ToTimeSpan(); +``` + +### 可为 null 的类型 + +C# 的 Protobuf 代码生成使用本机类型,如 `int` 表示 `int32`。 因此这些值始终包括在内,不能为 `null`。 + +对于需要显式 `null` 的值(例如在 C# 代码中使用 `int?`),Protobuf 的“已知类型”包括编译为可以为 null 的 C# 类型的包装器。 若要使用它们,请将 `wrappers.proto` 导入到 `.proto` 文件中,如以下代码所示: + +```protobuf +syntax = "proto3"; + +import "google/protobuf/wrappers.proto"; + +message Person { + // ... + google.protobuf.Int32Value age = 5; +} +``` + +`wrappers.proto` 类型不会在生成的属性中公开。 Protobuf 会自动将它们映射到 C# 消息中相应的可为 null 的 .NET 类型。 例如,`google.protobuf.Int32Value` 字段生成 `int?` 属性。 引用类型属性(如 `string` 和 `ByteString` )保持不变,但可以向它们分配 `null`,这不会引发错误。 + +下表完整列出了包装器类型以及它们的等效 C# 类型: + +| C# 类型 | 已知类型包装器 | +| :----------- | :---------------------------- | +| `bool?` | `google.protobuf.BoolValue` | +| `double?` | `google.protobuf.DoubleValue` | +| `float?` | `google.protobuf.FloatValue` | +| `int?` | `google.protobuf.Int32Value` | +| `long?` | `google.protobuf.Int64Value` | +| `uint?` | `google.protobuf.UInt32Value` | +| `ulong?` | `google.protobuf.UInt64Value` | +| `string` | `google.protobuf.StringValue` | +| `ByteString` | `google.protobuf.BytesValue` | + +### 小数 + +Protobuf 本身不支持 .NET `decimal` 类型,只支持 `double` 和 `float`。在 Protobuf 项目中,我们正在探讨这样一种可能性:将标准 decimal 类型添加到已知类型,并为支持它的语言和框架添加平台支持。尚未实现任何内容。 + +可以创建消息定义来表示 `decmial` 类型,以便在 .NET 客户端和服务器之间实现安全序列化。但其他平台上的开发人员必须了解所使用的格式,并能够实现自己对其的处理。 + +### 为 Protobuf 创建自定义 decimal 类型 + +```protobuf +package CustomTypes; + +message DecimalValue { + int64 units = 1; + + sfixed32 nanos = 2; +} +``` + +`nanos`字段表示从`0.999_999_999`到`-0.999_999_999`的值。例如,`decimal`值`1.5m`将表示`{ units = 1, nanos = 500_000_000 }`。这就是此示例中的 `nanos` 字段使用 `sfixed32` 类型的原因:对于较大的值,其编码效率比 `int32` 更高。 如果 `units` 字段为负,则 `nanos` 字段也应为负。 + +### 集合 + +#### 列表 + +Protobuf 中,在字段上使用`repeated`前缀关键字指定列表。以下示例演示如何创建列表: + +```protobuf +message Person { + // ... + repeated string roles = 8; +} +``` + +在生产的代码中,`repeated`字段由`Google.Protobuf.Collections.RepeatedField<T>`泛型类型表示。 + +```c# +public class Person +{ + // ... + public RepeatedField<string> Roles { get; } +} +``` + +`RepeatedField<T>`可实现 IList<T>。因此你可使用 LINQ 查询,或者将其转换为数组或列表。`RepeatedField<T>`属性没有公共 setter。项应添加到现有集合中。 + +```c# +var person = new Person(); + +person.Roles.Add("user"); + +var roles = new [] { "admin", "manager" }; +person.Roles.Add(roles); +``` + +#### 字典 + +.NET IDictionary<TKey, TValue> 类型在 Protobuf 中使用 `map<key_type, value_type>`表示。 + +```protobuf +message Person { + // ... + map<string, string> attributes = 9; +} +``` + +在生成的 .NET 代码中,`map`字段由`Google.Protobuf.Collections.MapField<TKey, TValue>`泛型类型表示。 + +`MapField<TKey, TValue>`可实现 IDictionary<TKey, TValue>。与`repeated`属性一样,`map`属性没有公共 setter。项应添加到现有集合中。 + +```c# +var person = new Person(); + +person.Attributes["create_by"] = "James"; + +var attributes = new Dictionary<string, string> +{ + ["last_modified"] = DateTime.UtcNow.ToString() +}; +person.Attributes.Add(attributes); +``` + +### 无结构的条件消息 + +Protobuf 是一种协定优先的消息传递格式。 构建应用时,必须在 `.proto` 文件中指定应用的消息,包括其字段和类型。 Protobuf 的协定优先设计非常适合强制执行消息内容,但可能会限制不需要严格协定的情况: + +- 包含未知有效负载的消息。 例如,具有可以包含任何消息的字段的消息。 +- 条件消息。 例如,从 gRPC 服务返回的消息可能是成功结果或错误结果。 +- 动态值。 例如,具有包含非结构化值集合的字段的消息,类似于 JSON。 + +Protobuf 提供语言功能和类型来支持这些情况。 + +#### 任意 + +利用`Any`类型,可以将消息作为嵌入类型使用,而无需`.proto`定义。若使用`Any`类型,请导入`any.proto`。 + +```protobuf +import "google/protobuf/any.proto"; + +message Status { + string message = 1; + google.protobuf.Any detail = 2; +} +``` + + + +```c# +// Create a status with a Person message set to detail. +var status = new ErrorStatus(); +status.Detail = Any.Pack(new Person { FirstName = "James" }); + +// Read Person message from detail. +if (status.Detail.Is(Person.Desciptor)) +{ + var person = status.Detail.Unpack<Person>(); + // ... +} +``` + + + +## Dubbo 3 + diff --git a/source/_posts/lucky.md b/source/_posts/lucky.md new file mode 100644 index 0000000..2d774ae --- /dev/null +++ b/source/_posts/lucky.md @@ -0,0 +1,5 @@ +--- +title: lucky +date: 2025-04-07 16:04:58 +tags: +--- diff --git a/source/_posts/macOS.md b/source/_posts/macOS.md new file mode 100644 index 0000000..2f1ef31 --- /dev/null +++ b/source/_posts/macOS.md @@ -0,0 +1,404 @@ +--- +title: macOS +date: 2025-09-26 22:54:01 +tags: +--- + +# 环境配置 + +## Homebrew + +> 官网:https://brew.sh/ + +### 介绍 + +Homebrew 就像 Mac 的智能软件管家。 + +- 你可以用它安装需要的软件(比如 Python、MySQL),它会自动下载、安装、配置,甚至帮你处理依赖(比如装 A 需要先装 B)。 +- 卸载时,它会把软件和相关文件清理干净,不留垃圾。 +- 支持下载命令行工具(如`git`)和图形应用(如`chrome`) + +### 安装 + +安装前需开启魔法,防止拉取失败。 + +```shell +/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" +``` + +### 常见命令 + +| 操作类型 | 命令 | 功能说明 | +| -------- | ----------------------------- | ------------------------------ | +| 安装 | brew install [package name] | 安装软件(自动处理依赖) | +| 卸载 | brew uninstall [package name] | 彻底卸载软件(并自动清理依赖) | +| 更新 | brew update | 更新 Homebrew 本体 | +| | brew update [package name] | 更新指定软件 | +| | brew upgrade | 更新所有已安装软件 | +| 查询 | brew info [package name] | 查看指定软件详细信息 | +| | brew list | 列出所有已安装软件 | +| 清理 | brew cleanup -n | 预览可清理的旧版本 | +| | brew cleanup [package name] | 清理指定软件的旧版本 | + +### 高阶技巧 + +#### 服务管理系统(类似 Linux 的 systemd) + +```shell +brew services start mysql # 启动 MySQL 服务 +brew services stop redis # 停止 Redis 服务 +brew services list # 查看所有服务状态 +``` + +#### 精准版本控制 + +```shell +brew install python@3.9 # 安装指定 Python 版本为 3.9 +brew pin python@3.9 # 锁定版本防止误升级 +brew unpin python@3.9 # 解除锁定 +``` + +### 卸载 + +```shell +# 卸载脚本(谨慎执行!) +/bin/bash -c "$(curl -fsSL <https://raw.githubusercontent.com/Homebrew/install/master/uninstall.sh>)" +``` + +### 目录结构解析 + +`Homebrew`在下载软件后,会将数据放在以下目录中 + +| **路径** | **作用** | +| ---------------------- | ---------------------------- | +| `/opt/homebrew` | ARM 芯片主目录(M1/M2 专用) | +| `/usr/local` | Intel 芯片主目录 | +| `/opt/homebrew/Cellar` | 所有安装的软件本体 | + +## oh my zsh + +通过下载脚本安装命令 + +```shell +sh -c "$(curl -fsSL https://gitee.com/pocmon/ohmyzsh/raw/master/tools/install.sh)" +``` + +剩余安装过程跟 `Linux`中的保持一致。 + +## nvm + +### 通过 Homebrew 安装 + +```shell +brew install nvm +``` + +这种方式需要手动配置环境变量。 + +`zsh`下编辑 `~/.zshrc`环境变量配置文件,文件末尾添加以下内容。 + +```ini +export NVM_DIR="$HOME/.nvm" +[ -s "/opt/homebrew/opt/nvm/nvm.sh" ] && \. "/opt/homebrew/opt/nvm/nvm.sh" +[ -s "/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm" ] && \. "/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm" +``` + +`:wq`保存,然后`source ~/.zshrc`即可应用。 + +### 安装最新版本 node + +```shell +nvm install node +``` + +安装后会自动`use`。 + +## colima + +### 介绍 + +colima 是 macOS 上的容器运行时。 + +### 安装 + +```shell +# Homebrew +brew install colima +``` + +启动服务 + +```shell +brew services start colima +``` + +启动 colima + +```shell +colima start +``` + +### 卸载 + +```shell +colima stop +``` + +```shell +colima delete +``` + +```shell +brew uninstall colima +``` + +```shell +rm -rf ~/.colima +``` + + + +### 运行时 + +初始启动时,Colima会使用默认为Docker的用户指定的运行时启动。 + +#### Docker + +Docker运行时需要Docker客户端。可以使用`Homebrew`安装。 + +```shell +brew install docker +``` + +Colima启动之后,您可以使用MacOS上的Docker客户端,没有其他设置。 + +若需使用容器编排,就还需要安装`Docker Compose` + +```shell +brew install docker-compose +``` + +创建符号链接 + +使 Docker 能够找到 Docker Compose + +```shell +mkdir -p ~/.docker/cli-plugins +ln -sfn /opt/homebrew/opt/docker-compose/bin/docker-compose ~/.docker/cli-plugins/docker-compose +``` + +启动`COlima`并指定使用`Docker`运行时。 + +```shell +colima start --runtime docker +``` + +### 配置 + +#### 镜像源 + +停止 Colima + +```shell +colima stop +``` + +编辑 Colima 配置文件 + +```shell +code ~/.colima/default/colima.yaml +``` + +在文件中找到 `docker: {}` 这个部分,添加或修改 `registry-mirrors` 字段,如下所示。如果文件里没有 `docker:` 部分,您可以手动在文件末尾添加。 + +```yaml +# ... colima.yaml 文件中的其他配置 ... + +# 添加或修改 docker 部分 +docker: + registry-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 + + # 您可以添加多个,按顺序尝试 + +# ... colima.yaml 文件中的其他配置 ... +``` + +修改后保存文件。 + +重新启动 Colima + +```shell +colima start +``` + +Colima 在启动时会读取配置,并自动生成虚拟机内的 `/etc/docker/daemon.json` 文件。 + +验证配置是否生效 + +```shell +# 进入 Colima 虚拟机 +colima ssh + +# 在虚拟机内,执行 docker info 命令并过滤出镜像源信息 +docker info | grep "Registry Mirrors" -A 2 +``` + +如果看到类似下面的输出,就证明配置成功了: + +```shell +Registry Mirrors: + https://docker.m.daocloud.io/ + https://docker.imgdb.de/ +``` + + + +## SVN + +### 安装 + +```shell +brew install svn +``` + + + +# 常用软件 + +## iTerm2 + +### 安装 + + + +### 配置 + +#### 主题 + +在当前用户目录`~`下新建一个专门用于存放配置文件的文件夹,例如 `~/.dotfiles` + +```shell +mkdir -p ~/.dotfiles +``` + +然后新建存放`iTerm2`的配置文件目录,并在其中创建存放主题的目录 + +```shell +mkdir -p ~/.dotfiles/iTerm-Settings/themes +``` + +拉取主题文件,例如`dracula` + +```shell +git clone https://github.com/dracula/iterm.git ~/.dotfiles/iTerm-Settings/themes/dracula +``` + +## Visual Studio Code + +### 配置 + +#### `Code` 命令 + +1. 打开 Visual Studio Code +2. 打开命令面板 (Command Palette) + 使用快捷键 `⌘ + Shift + P` (Command + Shift + P) 来打开命令面板。这是 VS Code 中最核心的功能入口。 +3. 运行安装命令 + 在弹出的命令面板输入框中,输入 `shell` 或者 `code`,它会自动筛选出相关命令。 找到并选择 **`Shell Command: Install 'code' command in PATH`** 这一项,然后按回车。 +4. 输入密码授权 + 系统可能会提示您输入当前 Mac 用户的登录密码,因为它需要权限在 `/usr/local/bin/` 目录下创建一个符号链接。按提示输入密码后回车即可。 如果成功,您会看到一个小的确认弹窗。 +5. 重启终端 + 关闭当前正在使用的所有终端窗口(无论是系统自带的 Terminal 还是 iTerm2),然后重新打开一个新的终端窗口。 这样做是为了让终端重新加载 `PATH` 环境变量,从而识别到新安装的 `code` 命令。 + +## HBuilderX + +### 常见问题 + +使用cli脚本创建uniapp 运行时报错“cli项目运行依赖本地的Nodejs环境,请先安装并配置到系统环境变量后重试。” + +编辑~/.bash_profile + +```bash + vim ~/.bash_profile +``` + +打开之后新增 + +```bash +#nvm环境 +export NVM_DIR="$HOME/.nvm" +[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm +[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion +``` + +保存退出后执行 + +```bash +source ~/.bash_profile +``` + +# 系统设置 + +## 键盘 + +### 关闭 首字母自动大写 功能 + +键盘 > 文字输入 > 输入法(ABC 和 简体拼音)> 编辑 > 关闭 自动大写字词的首字母 + +## 隐私与安全性 + +### 允许安装任何来源的应用程序 + +默认是不显示的,需通过终端,打开任何来源选项的显示 + +```shell +sudo spctl --master-disable +``` + +## 开启开发者模式 + +macOS每次在IDE开启debug的时候,都会出现Developer Tools Access的弹窗。 + +![72e12ae2-a46f-4f4e-819e-42cdc5ac1067](https://rustfs.wenyongdalucky.club:443/hexo/72e12ae2-a46f-4f4e-819e-42cdc5ac1067.png) + +### 解决方案 + +我们只需要启用开发者模式即可 + +打开终端输入下边命令: + +```shell +# 查看状态 +DevToolsSecurity --status +# 输入密码,修改为enable,即可用 +DevToolsSecurity --enable +# 输入密码,修改为disable,即关闭 +DevToolsSecurity --disable +``` + +# 快捷键 + +## 创建虚拟桌面 + +Control + 上箭头 + +## 切换虚拟桌面 + +Control + 左右箭头 + +## 访问 Mission Control + +F3 + +## 隐藏或显示程序呜 + +Option + Command + D + diff --git a/source/_posts/nodejs.md b/source/_posts/nodejs.md new file mode 100644 index 0000000..4589b23 --- /dev/null +++ b/source/_posts/nodejs.md @@ -0,0 +1,60 @@ +--- +title: nodejs +date: 2024-08-01 09:34:32 +tags: +--- + +# nvm + +## nvm介绍 + +nvm 一个nodejs版本管理工具! + + + +nvm全英文也叫node.js version management,是一个nodejs的版本管理工具。为了解决node.js各种版本存在不兼容现象可以通过它可以安装和切换不同版本的nodejs。 + +nvm能干嘛? + +简单的命令下载长期稳定支持版本的Nodejs + +简单的命令实现 Node.js 的多个版本之间轻松切换 + +## nvm命令 + +### 版本 + +```shell +nvm -v +``` + +### 查看可以nodejs的稳定版本 + +```shell +nvm list available +``` + +### 下载指定node版本 + +```shell +nvm install 18.20.4 +``` + +### 查看已安装nodejs版本 + +```shell +nvm list +``` + +### 切换当前使用版本 + +```shell +nvm ust 18.20.4 +``` + +### 卸载nodejs版本 + +```shell +nvm uninstall 18.20.4 +``` + diff --git a/source/_posts/ubuntu.md b/source/_posts/ubuntu.md new file mode 100644 index 0000000..399d50f --- /dev/null +++ b/source/_posts/ubuntu.md @@ -0,0 +1,638 @@ +--- +title: ubuntu +date: 2025-05-09 09:44:01 +tags: +--- + +# Server + +## 安装 + +默认选中「Try or Install Ubuntu Server」安装选项,回车(或等待 30 秒后),等待系统镜像自检并进行安装初始化。 + +![image-20250509094634889](https://rustfs.wenyongdalucky.club:443/hexo/image-20250509094634889.png) + +### 选择语言:English + +![image-20250509100201646](D:\source\repos\XiaodaBlogSource\source\_posts\ubuntu\image-20250509100201646.png) + +### 键盘默认:English + +![image-20250509100212670](D:\source\repos\XiaodaBlogSource\source\_posts\ubuntu\image-20250509100212670.png) + +### 安装类型:Ubuntu Server + +选择默认第一个(会自带一些组件,方便使用) + +![image-20250509100247973](D:\source\repos\XiaodaBlogSource\source\_posts\ubuntu\image-20250509100247973.png) + +### 网络配置 + +使用 DHCP 或者 静态IP (建议这里设置好 静态IP,如果选择 DHCP,则在此界面直接选择Done 后即可) + +![image-20250509100604701](D:\source\repos\XiaodaBlogSource\source\_posts\ubuntu\image-20250509100604701.png) + +静态IP 选择 Edit IPv4 + +![image-20250509100656239](D:\source\repos\XiaodaBlogSource\source\_posts\ubuntu\image-20250509100656239.png) + +然后选择 Manual + +![image-20250509100804038](D:\source\repos\XiaodaBlogSource\source\_posts\ubuntu\image-20250509100804038.png) + +![image-20250509102609874](D:\source\repos\XiaodaBlogSource\source\_posts\ubuntu\image-20250509102609874.png) + +### 代理配置 + +**Configure proxy配置页面的Proxy address无需配置** + +![image-20250509102734539](D:\source\repos\XiaodaBlogSource\source\_posts\ubuntu\image-20250509102734539.png) + +### 镜像源配置 + +默认清华源 + +![image-20250509102858753](D:\source\repos\XiaodaBlogSource\source\_posts\ubuntu\image-20250509102858753.png) + +### 安装磁盘配置 + +**选择安装磁盘,直接回车默认自动分配,需要手动分区的话选择 [custom storage layout]** + +![image-20250509111350269](D:\source\repos\XiaodaBlogSource\source\_posts\ubuntu\image-20250509111350269.png) + +选择 **custom storage layout** + +![image-20250509112338500](D:\source\repos\XiaodaBlogSource\source\_posts\ubuntu\image-20250509112338500.png) + +![image-20250509112354306](D:\source\repos\XiaodaBlogSource\source\_posts\ubuntu\image-20250509112354306.png) + +首先分配swap分区:一般基于物理内存的 2-4倍 + +![image-20250509112453286](D:\source\repos\XiaodaBlogSource\source\_posts\ubuntu\image-20250509112453286.png) + +/boot 分区,一般2G足以 + +/ 根分区,分配剩余空间 + +![image-20250509112822681](https://rustfs.wenyongdalucky.club:443/hexo/image-20250509112822681.png) + +### 设置计算机名及用户名 + +![image-20250509113002925](https://rustfs.wenyongdalucky.club:443/hexo/image-20250509113002925.png) + +### 是否升级 Ubuntu Pro + +直接默认跳过即可 +![image-20250509121748189](https://rustfs.wenyongdalucky.club:443/hexo/image-20250509121748189.png) + +### 安装 OpenSSH 服务 + +![image-20250509121806128](D:\source\repos\XiaodaBlogSource\source\_posts\ubuntu\image-20250509121806128.png) + +### 选择预置环境 + +按需选取,不需要则直接选择 Done 回车继续 + +![image-20250509121923077](D:\source\repos\XiaodaBlogSource\source\_posts\ubuntu\image-20250509121923077.png) + +安装系统中 + +![image-20250509122057921](D:\source\repos\XiaodaBlogSource\source\_posts\ubuntu\image-20250509122057921.png) + +安装完成后重启即可 + +![image-20250509122413007](D:\source\repos\XiaodaBlogSource\source\_posts\ubuntu\image-20250509122413007.png) + +重启完成,进入系统 + +![image-20250509123500684](D:\source\repos\XiaodaBlogSource\source\_posts\ubuntu\image-20250509123500684.png) + +## 配置网络 + +![image-20250509124052044](D:\source\repos\XiaodaBlogSource\source\_posts\ubuntu\image-20250509124052044.png) + +```shell +cd /etc/netplan +ls +# 编辑当前目录下以yaml扩展名的网卡配置文件 +sudo vim 50-cloud-init.yaml +``` + +文件内容 + +```shell +network: + version: 2 + ethernets: + enp0s3: + dhcp4: true +``` + +在VirtualBox中工具->网络中 增加仅主机(Host-Only)网络 + +![image-20250509124733922](D:\source\repos\XiaodaBlogSource\source\_posts\ubuntu\image-20250509124733922.png) + +网卡如果要是DHCP就选自动配置网卡,否则手动分配就选手动配置网卡 + +如果选DHCP,还需要启动服务器 + +![image-20250509124838460](D:\source\repos\XiaodaBlogSource\source\_posts\ubuntu\image-20250509124838460.png) + +配置好后,在对应虚拟机中,添加好网卡,连接方式选择仅主机(Host-Only)网络,名称选择刚刚在工具中配置的 + +![image-20250509125003457](D:\source\repos\XiaodaBlogSource\source\_posts\ubuntu\image-20250509125003457.png) + +以上修改需要先重启虚拟机 + +查看是否生效,需要执行`ip a`命令 + +看是否有网卡名称为`enp0s8` + +紧接着回到刚刚在`/etc/netplan`目录,下编辑的网卡配置文件`50-cloud-init.yaml` + +增加`enp0s8`,若是自动分配网络,则直接`dhcp4: true`即可,否则按一下分配`addressed`,手动分配一个根据子网的ipv4地址,并将`dhcp4`设置为`false` + +```shell +network: + version: 2 + ethernets: + enp0s3: + dhcp4: true + enp0s8: + addresses: [192.168.56.35/24] + dhcp4: no + +``` + +`:wq`保存后,执行一下命令 + +```shell +sudo netplan generate +sudo netplan apply +``` + +若不报错,则修改成功,再执行`ip a`查看网卡信息 + +![image-20250509125542444](D:\source\repos\XiaodaBlogSource\source\_posts\ubuntu\image-20250509125542444.png) + +ip地址已经生效,可以在主机里 ping 一下 + +## 安装GUI + +默认情况下,Ubuntu Server不包括图形用户界面(GUI)。GUI占用了用于面向服务器的任务的系统资源(内存和处理器)。但是,某些任务和应用程序在GUI环境中更易于管理并且可以更好地工作。 + +### 更新存储库和软件包 + +首先更新存储库和软件包列表: + +```shell +sudo apt-get update && sudo apt-get upgrade +``` + +这样可以确保正在使用最新的软件更新。 + +接下来,安装**tasksel manager**实用程序: + +```shell +sudo apt install tasksel +``` + +> **注意:** **Tasksel**是用于一次安装多个相关软件包的实用程序。有关更多详细信息,请参见[文档](https://help.ubuntu.com/community/Tasksel)。 + +### 选择一个显示管理器 + +显示管理器是启动显示服务器,启动桌面并管理用户身份验证的应用程序。默认的**GDM3**(与[KDE-Plasma一起使用](https://kde.org/plasma-desktop))是资源密集型显示管理器。如果需要节省系统资源,请考虑使用更浅的显示管理器,例如**SDDM**,**SLiM**或**LightDM**。 + +默认情况下,只有一个显示管理器可以管理服务器。仅当配置为管理其他服务器时,它们才能同时运行。本文假定您将使用单个默认显示管理器。 + +显示当前使用的显示管理器: + +```shell +cat /etc/X11/default-display-manager +``` + +要安装特定的显示管理器,请使用**apt-get**程序包管理器: + +要安装SLiM: + +```shell +sudo apt-get install slim +``` + +要安装LightDM: + +```shell +sudo apt-get install lightdm +``` + +该**SDDM**显示管理器可从安装**中的tasksel** KDE的安装过程中的菜单。 + +切换显示管理器 + +```shell +sudo dpkg-reconfigure gdm3 +``` + + + +### 选择服务器的GUI + +GNOME是大多数Ubuntu安装的默认GUI,并且(宽松地)基于Apple生态系统。 + +KDE是另一种流行的GUI,(宽松地)基于Microsoft生态系统。如果要具有常规Ubuntu系统的外观,请选择以下桌面环境之一。 + +#### GNOME + +要安装GNOME,请首先启动**taskel**: + +``` +tasksel +``` + +将会启动一个彩色界面。使用箭头键向下滚动列表,找到**Ubuntu桌面**。 + +使用**空格**键将其选中`ubuntu-desktop`,然后按**Tab**键选择底部的**确定**,然后按**Enter键**。 + +> 要使用`ubuntu-desktop`,需事先安装好,通过 sudo apt install -y ubuntu-desktop + +系统将安装软件并重新引导,为您提供由默认显示管理器生成的图形登录屏幕。在我们的例子中是SLiM。 + +输入您的**登录凭据**。如果您安装了多个接口,请使用**F1**在GUI之间切换。 + +卸载ubuntu-desktop + +```shell +sudo apt remove -y --purge ubuntu-desktop +sudo apt-get autoremove +sudo snap remove thunderbird +sudo snap remove gnome-42-2204 +sudo snap remove firefox +sudo snap remove gtk-common-themes +sudo apt-get remove gnome-tweak-tool +sudo apt-get remove gnome-shell +``` + + + +#### KDE Plasma + +要安装KDE Plasma,请使用以下Linux命令: + +``` +sudo apt-get install kde-plasma-desktop +``` + +在安装过程中可能会提示您选择默认显示管理器。使用箭头键进行选择,然后按**Enter**。 + +使用以下命令启动KDE Plasma: + +``` +sudo service display_manager start +``` + +代替*display_manager,* 输入已安装的显示管理器的名称(例如SLiM,lightDM,SDDM)。输入您的凭据并登录。 + +> **注意:**这些传统的Ubuntu Server GUI应用程序需要大量的系统资源。它们可能会影响服务器的功能。如果需要最大程度地利用服务器资源,请考虑下面列出的较轻的GUI应用程序之一。 + +#### Mate 服务器核心桌面 + +**Mate**是一种流行的轻量级图形界面。通过执行以下命令进行安装: + +``` +sudo tasksel install ubuntu-mate-core +``` + +等待taskel完成操作。完成后,使用以下命令启动桌面界面: + +``` +sudo service display_manager start +``` + +代替*display_manager,* 输入已安装的显示管理器的名称(例如SLiM,lightDM,SDDM)。系统将提示您登录。 + +要退出GUI,请打开命令行并输入: + +``` +sudo service display_manager stop +``` + +> **注意**:您可能需要按**Ctrl-Alt-F1**才能返回到服务器的命令行界面。另请注意,此过程会自动安装并使用**lightdm**显示管理器。 + +#### Lubuntu核心服务器桌面 + +Lubuntu是一个非常轻量级的GUI。如果需要图形界面,但要最大程度地减少对系统内存和处理器的影响,请使用此应用程序。 + +要安装Lubuntu,请输入以下内容: + +``` +sudo tasksel install lubuntu-core +``` + +或者,您可以使用以下命令从**taskel**菜单安装: + +``` +tasksel +``` + +使用箭头键突出显示要使用的Lubuntu GUI。按**空格**键进行选择,然后按**Tab键**至**OK**按钮,然后按**Enter键**。 + +使用以下命令启动GUI: + +``` +sudo service display_manager start +``` + +通过打开终端窗口并输入以下内容来退出GUI: + +``` +sudo service display_manager stop +``` + +#### Xubuntu服务器核心桌面 + +Xubuntu是使用Xfce桌面环境的Ubuntu的派生版本。 + +通过输入以下命令安装Xubuntu: + +``` +sudo tasksel install xubuntu-core +``` + +输入以下命令启动它: + +``` +sudo service display_manager start +``` + +#### Xfce桌面 + +Xfce桌面环境可以单独使用。它被设计为响应迅速,轻巧且用户友好。 + +使用以下命令安装Xfce: + +``` +sudo apt-get install xfce4 slim +``` + +输入以下命令启动Xfce: + +``` +sudo service slim start +``` + +> **注意:** Xfce GUI仅与SLiM显示管理器兼容。 + +### 在GUI之间切换 + +如果安装了多个GUI,则可以选择要使用的GUI。启动显示管理器后,将提示您登录。但是,该屏幕还允许您选择要启用的GUI。 + +通过按**F1**在GUI之间切换。该界面将在会话(或GUI)之间切换。切换到所选的GUI后登录。 + +### 关闭与打开GUI + +#### 关闭 + +```shell +sudo systemctl set-default multi-user.target +sudo reboot +``` + +#### 打开 + +```shell +sudo systemctl set-default graphical.target +sudo reboot +``` + +## 远程桌面配置 + +### RDP + +Windows 带有一个非常方便的功能,称为远程桌面连接,它使用 RDP 协议远程连接 PC。虽然在建立从 Windows 到 Windows 系统的远程桌面连接时使用起来非常容易,但对于 Linux 系统来说就不一样了。这是因为Linux默认没有安装RDP协议。在这种情况下,我们必须在 Linux 系统上手动执行一些配置来启用 RDP,在本指南中我们知道如何做到这一点。 + +什么是XRDP? + +> XRDP 是一个免费的开源程序,是 Microsoft RDP(远程桌面协议)的实现,可通过 GUI 轻松远程访问 Linux 系统。使用 XRDP,可以登录到远程 Linux 计算机并创建一个真实的桌面会话,就像您登录到本地计算机一样。 + +#### 执行存储库更新 + +```shell +sudo apt update +``` + +#### 在 Ubuntu 24.04.2 LTS 上安装XRDP + +我们知道 Ubuntu 没有像 Windows 操作系统那样安装 RDP,因此,我们需要在我们的 Linux 系统上安装 RDP 的开源实现 XRDP。好在我们不需要添加任何第三方存储库,因为它可以使用系统默认安装。 + +```shell +sudo apt install xrdp +``` + +#### 启动并启用 XRDP 服务 + +要在系统启动时自动启动并启用 XRDP 服务,请使用给定的命令: + +启动它: + +```shell +sudo systemctl start xrdp +``` + +开机并启用它: + +```shell +sudo systemctl enable xrdp +``` + +检查状态: + +```shell +systemctl status xrdp +``` + +#### 在防火墙中放行3389端口 + +要让网络中的其他系统通过 RDP 远程访问 Ubuntu 24.04.2 LTS,请在系统防火墙上放行端口号 3389。 + +```shell +sudo ufw allow from any to any port 3389 proto tcp +``` + +![image-20250721085911086](D:\source\repos\XiaodaBlogSource\source\_posts\ubuntu\image-20250721085911086-1753248149581-28.png) + +**接下来**,查看你的 Ubuntu 系统的 IP 地址,并在某处记下它。在您的终端上运行: + +```shell +ip a +``` + +#### KDE Plasma解决XRDP无法连接的问题 + +> [Installing KDE Plasma and XRDP Service on Remote Ubuntu 22](https://www.vps-mart.com/blog/install-kde-plasma-and-xrdp-service-on-remote-ubuntu) + +通过XRDP登录时启动KDE等离子会话。 CAT命令用于创建具有指定环境变量的.xsessionrc文件。执行这些命令后,将使用指定内容创建或覆盖〜/.xsession和〜/.xsessionRC文件。这些文件通常在X会话启动过程中用于设置环境变量并定义要执行的启动命令或脚本。 + +```shell +echo "/usr/bin/startplasma-x11" > ~/.xsession +D=/usr/share/plasma:/usr/local/share:/usr/share:/var/lib/snapd/desktop +C=/etc/xdg/xdg-plasma:/etc/xdg +C=${C}:/usr/share/kubuntu-default-settings/kf5-settings +cat < ~/.xsessionrc +export XDG_SESSION_DESKTOP=KDE +export XDG_DATA_DIRS=${D} +export XDG_CONFIG_DIRS=${C} +EOF +``` + +重启 XRDP服务 + +```shell +systemctl restart xrdp +``` + +## 更换时区 + +### 查看当前时区: + +```shell +timedatectl +``` + +类似输出 + +```shell + Local time: Mon 2025-07-21 01:05:37 UTC + Universal time: Mon 2025-07-21 01:05:37 UTC + RTC time: Mon 2025-07-21 01:05:37 + Time zone: Etc/UTC (UTC, +0000) +System clock synchronized: yes + NTP service: active + RTC in local TZ: no + +``` + +### 列出所有可用时区: + +```shell +timedatectl list-timezones +``` + +时区太多了,可以使用管道过滤查找你所在的城市,例如: + +```shell +timedatectl list-timezones | grep Shanghai +``` + +输出: +```shell +Asia/Shanghai +``` + +### 设置新的时区: + +例如,将时区设置为 **上海(北京时间)** : + +```shell +sudo timedatectl set-timezone Asia/Shanghai +``` + +#### 再次查看时区确认是否更改成功: + +```shell +timedatectl +``` + +## snap包管理器 + +> [Linux snap 命令 | 菜鸟教程](https://www.runoob.com/linux/linux-comm-snap.html) + +### 安装snap软件 + +```shell +sudo snap install hello-world +``` + +### 卸载snap软件 + +```shell +sudo snap remove hello-world +``` + +### 查看已安装的snap软件 + +```shell +snap list +``` + + + +### 解决snap包管理器下载报错 x509: certificate signed by unknown authority + +#### 代理方式 + +```shell +sudo snap set system proxy.https="http://127.0.0.1:7890" +sudo snap set system proxy.http="http://127.0.0.1:7890" +``` + +然后可以正常安装: + +```shell +sudo snap install hello-world +``` + +## 禁止自动挂起 + +```shell +Broadcast message from user@ubuntu-ai-dev-server (Tue 2025-07-22 15:34:31 CST): + +The system will suspend now! +``` + +### 解决方式 + +```shell +sudo vim /etc/systemd/logind.conf +``` + +将里面的 + +```shell +IdleAction=suspend +``` + +改为 + +```shell +IdleAction=ignore +``` + +# apt包管理器 + +## 安装Fastfetch + +**添加 Fastfetch PPA:** + +```Bash +sudo add-apt-repository ppa:zhangsongcui3371/fastfetch +``` + +这条命令会提示并按回车确认添加 PPA。 + +**更新软件包列表:** 添加 PPA 后,需要更新软件包列表,以便系统能够识别新添加的软件源。 + +```Bash +sudo apt update +``` + +**安装 Fastfetch:** 现在,可以安装 Fastfetch 了: + +```Bash +sudo apt install fastfetch +``` + +**运行 Fastfetch:** 安装完成后,可以在终端中输入 `fastfetch` 来运行它: + +```Bash +fastfetch +``` diff --git a/source/_posts/ubuntu/image-20250509094634889.png b/source/_posts/ubuntu/image-20250509094634889.png new file mode 100644 index 0000000..877de28 Binary files /dev/null and b/source/_posts/ubuntu/image-20250509094634889.png differ diff --git a/source/_posts/ubuntu/image-20250509100201646.png b/source/_posts/ubuntu/image-20250509100201646.png new file mode 100644 index 0000000..c2b5341 Binary files /dev/null and b/source/_posts/ubuntu/image-20250509100201646.png differ diff --git a/source/_posts/ubuntu/image-20250509100212670.png b/source/_posts/ubuntu/image-20250509100212670.png new file mode 100644 index 0000000..7ea2f16 Binary files /dev/null and b/source/_posts/ubuntu/image-20250509100212670.png differ diff --git a/source/_posts/ubuntu/image-20250509100247973.png b/source/_posts/ubuntu/image-20250509100247973.png new file mode 100644 index 0000000..a586000 Binary files /dev/null and b/source/_posts/ubuntu/image-20250509100247973.png differ diff --git a/source/_posts/ubuntu/image-20250509100604701.png b/source/_posts/ubuntu/image-20250509100604701.png new file mode 100644 index 0000000..b821012 Binary files /dev/null and b/source/_posts/ubuntu/image-20250509100604701.png differ diff --git a/source/_posts/ubuntu/image-20250509100656239.png b/source/_posts/ubuntu/image-20250509100656239.png new file mode 100644 index 0000000..6485e6a Binary files /dev/null and b/source/_posts/ubuntu/image-20250509100656239.png differ diff --git a/source/_posts/ubuntu/image-20250509100804038.png b/source/_posts/ubuntu/image-20250509100804038.png new file mode 100644 index 0000000..7ac92b3 Binary files /dev/null and b/source/_posts/ubuntu/image-20250509100804038.png differ diff --git a/source/_posts/ubuntu/image-20250509102609874.png b/source/_posts/ubuntu/image-20250509102609874.png new file mode 100644 index 0000000..8b51247 Binary files /dev/null and b/source/_posts/ubuntu/image-20250509102609874.png differ diff --git a/source/_posts/ubuntu/image-20250509102734539.png b/source/_posts/ubuntu/image-20250509102734539.png new file mode 100644 index 0000000..82d0a99 Binary files /dev/null and b/source/_posts/ubuntu/image-20250509102734539.png differ diff --git a/source/_posts/ubuntu/image-20250509102858753.png b/source/_posts/ubuntu/image-20250509102858753.png new file mode 100644 index 0000000..0ae1f3e Binary files /dev/null and b/source/_posts/ubuntu/image-20250509102858753.png differ diff --git a/source/_posts/ubuntu/image-20250509111350269.png b/source/_posts/ubuntu/image-20250509111350269.png new file mode 100644 index 0000000..ee56740 Binary files /dev/null and b/source/_posts/ubuntu/image-20250509111350269.png differ diff --git a/source/_posts/ubuntu/image-20250509112338500.png b/source/_posts/ubuntu/image-20250509112338500.png new file mode 100644 index 0000000..3008050 Binary files /dev/null and b/source/_posts/ubuntu/image-20250509112338500.png differ diff --git a/source/_posts/ubuntu/image-20250509112354306.png b/source/_posts/ubuntu/image-20250509112354306.png new file mode 100644 index 0000000..a21fd33 Binary files /dev/null and b/source/_posts/ubuntu/image-20250509112354306.png differ diff --git a/source/_posts/ubuntu/image-20250509112453286.png b/source/_posts/ubuntu/image-20250509112453286.png new file mode 100644 index 0000000..72055ef Binary files /dev/null and b/source/_posts/ubuntu/image-20250509112453286.png differ diff --git a/source/_posts/ubuntu/image-20250509112822681.png b/source/_posts/ubuntu/image-20250509112822681.png new file mode 100644 index 0000000..5fdbc52 Binary files /dev/null and b/source/_posts/ubuntu/image-20250509112822681.png differ diff --git a/source/_posts/ubuntu/image-20250509113002925.png b/source/_posts/ubuntu/image-20250509113002925.png new file mode 100644 index 0000000..212fd8f Binary files /dev/null and b/source/_posts/ubuntu/image-20250509113002925.png differ diff --git a/source/_posts/ubuntu/image-20250509121748189.png b/source/_posts/ubuntu/image-20250509121748189.png new file mode 100644 index 0000000..5abb81b Binary files /dev/null and b/source/_posts/ubuntu/image-20250509121748189.png differ diff --git a/source/_posts/ubuntu/image-20250509121806128.png b/source/_posts/ubuntu/image-20250509121806128.png new file mode 100644 index 0000000..0fd9430 Binary files /dev/null and b/source/_posts/ubuntu/image-20250509121806128.png differ diff --git a/source/_posts/ubuntu/image-20250509121923077.png b/source/_posts/ubuntu/image-20250509121923077.png new file mode 100644 index 0000000..82df102 Binary files /dev/null and b/source/_posts/ubuntu/image-20250509121923077.png differ diff --git a/source/_posts/ubuntu/image-20250509122057921.png b/source/_posts/ubuntu/image-20250509122057921.png new file mode 100644 index 0000000..4d1eff5 Binary files /dev/null and b/source/_posts/ubuntu/image-20250509122057921.png differ diff --git a/source/_posts/ubuntu/image-20250509122413007.png b/source/_posts/ubuntu/image-20250509122413007.png new file mode 100644 index 0000000..15c6c18 Binary files /dev/null and b/source/_posts/ubuntu/image-20250509122413007.png differ diff --git a/source/_posts/ubuntu/image-20250509123500684.png b/source/_posts/ubuntu/image-20250509123500684.png new file mode 100644 index 0000000..a5970bd Binary files /dev/null and b/source/_posts/ubuntu/image-20250509123500684.png differ diff --git a/source/_posts/ubuntu/image-20250509124052044.png b/source/_posts/ubuntu/image-20250509124052044.png new file mode 100644 index 0000000..8b7bfb9 Binary files /dev/null and b/source/_posts/ubuntu/image-20250509124052044.png differ diff --git a/source/_posts/ubuntu/image-20250509124733922.png b/source/_posts/ubuntu/image-20250509124733922.png new file mode 100644 index 0000000..4162859 Binary files /dev/null and b/source/_posts/ubuntu/image-20250509124733922.png differ diff --git a/source/_posts/ubuntu/image-20250509124838460.png b/source/_posts/ubuntu/image-20250509124838460.png new file mode 100644 index 0000000..5c0315d Binary files /dev/null and b/source/_posts/ubuntu/image-20250509124838460.png differ diff --git a/source/_posts/ubuntu/image-20250509125003457.png b/source/_posts/ubuntu/image-20250509125003457.png new file mode 100644 index 0000000..e68db9f Binary files /dev/null and b/source/_posts/ubuntu/image-20250509125003457.png differ diff --git a/source/_posts/ubuntu/image-20250509125542444.png b/source/_posts/ubuntu/image-20250509125542444.png new file mode 100644 index 0000000..0b9d768 Binary files /dev/null and b/source/_posts/ubuntu/image-20250509125542444.png differ diff --git a/source/_posts/ubuntu/image-20250721085911086-1753248149581-28.png b/source/_posts/ubuntu/image-20250721085911086-1753248149581-28.png new file mode 100644 index 0000000..53f3bd1 Binary files /dev/null and b/source/_posts/ubuntu/image-20250721085911086-1753248149581-28.png differ diff --git a/source/_posts/大数据.md b/source/_posts/大数据.md new file mode 100644 index 0000000..a12b8fb --- /dev/null +++ b/source/_posts/大数据.md @@ -0,0 +1,353 @@ +--- +title: 大数据 +date: 2025-08-03 22:53:51 +tags: +--- + +# Apache Doris + +## 简介 + +## 安装 + +### Docker Compose 方式 + +> [Quick Start - Apache Doris](https://doris.apache.org/docs/dev/gettingStarted/quick-start) + +新建用于Docker Compose 集群编排启动目录`mkdir -p /OLAP/doris` + +```bash +mkdir -p /OLAP/doris +``` + +编写启动脚本`start-doris.sh` + +> https://doris.apache.org/files/start-doris.sh + +```sh +#!/bin/bash + +# Default version +DORIS_QUICK_START_VERSION="2.1.9" + +# Parse parameters +while getopts "v:" opt; do + case $opt in + v) DORIS_QUICK_START_VERSION="$OPTARG" + ;; + \?) echo "Invalid option: -$OPTARG" >&2 + exit 1 + ;; + esac +done + +# Check system type +OS_TYPE=$(uname -s) +if [[ "$OS_TYPE" != "Linux" && "$OS_TYPE" != "Darwin" ]]; then + echo "Error: Unsupported operating system [$OS_TYPE], only Linux and Mac are supported" + exit 1 +fi + +# Check Docker environment +if ! command -v docker &> /dev/null; then + echo "Error: Docker environment not detected, please install Docker first" + exit 1 +fi + +# Check docker-compose +COMPOSE_CMD="" +if command -v docker-compose &> /dev/null; then + COMPOSE_CMD="docker-compose" +elif docker compose version &> /dev/null; then + COMPOSE_CMD="docker compose" +else + echo "Error: docker-compose plugin or docker-compose command is required" + exit 1 +fi + +# Generate docker-compose configuration for corresponding system +if [[ "$OS_TYPE" == "Linux" ]]; then + cat > docker-compose-doris.yaml <<EOF +version: "3" +services: + fe: + image: apache/doris:fe-${DORIS_QUICK_START_VERSION} + hostname: fe + environment: + - FE_SERVERS=fe1:127.0.0.1:9010 + - FE_ID=1 + network_mode: host + volumes: + - /mnt/app/doris/fe/doris-meta/:/opt/apache-doris/fe/doris-meta/ + - /mnt/app/doris/fe/log/:/opt/apache-doris/fe/log/ + be: + image: apache/doris:be-${DORIS_QUICK_START_VERSION} + hostname: be + environment: + - FE_SERVERS=fe1:127.0.0.1:9010 + - BE_ADDR=127.0.0.1:9050 + depends_on: + - fe + network_mode: host + volumes: + - /mnt/app/doris/be/storage/:/opt/apache-doris/be/storage/ + - /mnt/app/doris/be/script/:/docker-entrypoint-initdb.d/ +EOF +else # Mac system + cat > docker-compose-doris.yaml <<EOF +version: "3" +networks: + custom_network: + driver: bridge + ipam: + config: + - subnet: 172.20.80.0/24 + +services: + fe: + image: apache/doris:fe-${DORIS_QUICK_START_VERSION} + hostname: fe + ports: + - 8030:8030 + - 9030:9030 + - 9010:9010 + environment: + - FE_SERVERS=fe1:172.20.80.2:9010 + - FE_ID=1 + networks: + custom_network: + ipv4_address: 172.20.80.2 + + be: + image: apache/doris:be-${DORIS_QUICK_START_VERSION} + hostname: be + ports: + - 8040:8040 + - 9050:9050 + environment: + - FE_SERVERS=fe1:172.20.80.2:9010 + - BE_ADDR=172.20.80.3:9050 + depends_on: + - fe + networks: + custom_network: + ipv4_address: 172.20.80.3 +EOF +fi + +# Start services +$COMPOSE_CMD -f docker-compose-doris.yaml up -d + +echo "Doris cluster started successfully, version: ${DORIS_QUICK_START_VERSION}" +echo "You can manage the cluster using the following commands:" +echo " Stop cluster: $COMPOSE_CMD -f docker-compose-doris.yaml down" +echo " View logs: $COMPOSE_CMD -f docker-compose-doris.yaml logs -f" +echo " Connect to cluster: mysql -uroot -P9030 -h127.0.0.1" + +# Display connection information based on system type +if [[ "$OS_TYPE" == "Linux" ]]; then + echo -e "\nAccess FE/BE http ports (8030, 8040) using the following addresses (Linux system):" + echo " http://127.0.0.1:8030" + echo " http://127.0.0.1:8040" +elif [[ "$OS_TYPE" == "Darwin" ]]; then + echo -e "\nAccess FE/BE http ports (8030, 8040) using the following addresses (Mac system):" + echo " http://docker.for.mac.localhost:8030" + echo " http://docker.for.mac.localhost:8040" + echo "Note: If access fails, try using 127.0.0.1 address:" + echo " http://127.0.0.1:8030" + echo " http://127.0.0.1:8040" +fi +``` + +赋予权限 + +```bash +chmod 755 start-doris.sh +``` + +启动集群 + +```bash +./start-doris.sh +``` + +可以指定启动的版本通过`-v`参数,比如: + +```bash +./start-doris.sh -v 2.1.8 +``` + +使用MySQL客户端连接到集群并检查集群状态 + +```bash +## Check the FE status to ensure that both the Join and Alive columns are true. +mysql -uroot -P9030 -h127.0.0.1 -e 'SELECT `host`, `join`, `alive` FROM frontends()' ++-----------+------+-------+ +| host | join | alive | ++-----------+------+-------+ +| 127.0.0.1 | true | true | ++-----------+------+-------+ + +## Check the BE status to ensure that the Alive column is true. +mysql -uroot -P9030 -h127.0.0.1 -e 'SELECT `host`, `alive` FROM backends()' ++-----------+-------+ +| host | alive | ++-----------+-------+ +| 127.0.0.1 | 1 | ++-----------+-------+ + +``` + + + +### 配置 + +#### Linux系统环境配置(所有节点均需配置) + +##### 调大文件操作配置 + +```bash +vim /etc/security/limits.conf +``` + +在文件最后添加下面几行信息(注意 * 也要复制进去) + +```conf +* soft nofile 65536 +* hard nofile 65536 +* soft nproc 65536 +* hard nproc 65536 +``` + +可使用以下命令直接追加到配置文件内 + +```bash +echo "* soft nofile 65536" >> /etc/security/limits.conf +echo "* hard nofile 65536" >> /etc/security/limits.conf +echo "* soft nproc 65536" >> /etc/security/limits.conf +echo "* hard nproc 65536" >> /etc/security/limits.conf +``` + +保存完成需`reboot`,也可临时生效,免重启 + +```bash +ulimit -n 65536 +``` + +# Datax + +## MySQL离线同步至Apache Doris + +### 单表同步 + +新建配置文件`mysql_2_doris_t_base_material.json` + +```json +{ + "job": { + "setting": { + "speed": { + "channel": 1 + } + }, + "content": [ + { + "reader": { + "name": "mysqlreader", + "parameter": { + "username": "root", + "password": "123456", + "column": [ + "ID", "PID", "CODE", "SHORTCODE", "NAME", "FULLNAME", + "ISDETAIL", "CHILDCOUNT", "PYCODE", "LVL", "FLAG", "MODEL", + "ISSTANDARD", "AUXCLASSID", "ERPCLSID", "UNITGROUPID", "UNITID", + "DEFAULTLOC", "SPID", "QTYDECIMAL", "SECINV", "MTYPE", "MVER", + "MATERIAL", "FIRM", "FIRMORDER", "FACEDEAL", "SHAPESIZE", + "TECHDESC", "UNITWEIGHT", "MCODE", "MCAT", "COSTITEM", + "ORDERRECTOR", "POHIGHPRICE", "POHGHPRCMNYTYPE", "WWHGHPRC", + "WWHGHPRCMNYTYPE", "SOLOWPRC", "SOLOWPRCMNYTYPE", "TRACK", + "PRICEDECIMAL", "ACCTID", "SALEACCTID", "COSTACCTID", + "DEFAULTROUTINGID", "DEFAULTWORKTYPEID", "PRODUCTPRINCIPAL", + "PLANNER", "ISBACKFLUSH", "MRPCON", "MRPORDER", "CHARTNUMBER", + "INSPECTIONLEVEL", "PROCHKMDE", "WWCHKMDE", "SOCHKMDE", + "WTHDRWCHKMDE", "STKCHKMDE", "OTHERCHKMDE", "INSPECTIONPROJECT", + "NAMEEN", "MODELEN", "HSNUMBER", "IMPOSTTAXRATE", + "CONSUMETAXRATE", "STARTSERVICE", "MAKEFILE", "ISFIX", + "TTERMOFSERVICE", "TTERMOFUSEFULTIME", "PRODUCTNO", "PARAMVALUE", + "MEMO", "CDATE", "CUSERID", "CSTAFFNAME", "MDATE", "MUSERID", + "MSTAFFNAME", "K_ID", "K_PID", "DDATE", "DUSERID", "DSTAFFNAME", + "K_CODE", "AMODEL", "K_COSTITEM", "DWGSYMDESC", "SPDESC", + "CDRATE", "GWEIGHT", "NWEIGHT", "L", "W", "H", "MSIZE", + "K_COST", "BATCHAPPLICANT", "MATSLUGGISHSTATUS", + "SLUGGISHEFFECTIVEDATE", "K_AUTO_ID", "FRATE", "K3CLOUDID" + ], + "connection": [ + { + "table": ["t_base_material"], + "jdbcUrl": ["jdbc:mysql://192.168.6.35:3306/erp?useSSL=false&serverTimezone=Asia/Shanghai"], + "selectedDatabase": "erp" + } + ] + } + }, + "writer": { + "name": "doriswriter", + "parameter": { + "loadUrl": ["192.168.6.35:8040"], + "username": "root", + "password": "", + "column": [ + "ID", "PID", "CODE", "SHORTCODE", "NAME", "FULLNAME", + "ISDETAIL", "CHILDCOUNT", "PYCODE", "LVL", "FLAG", "MODEL", + "ISSTANDARD", "AUXCLASSID", "ERPCLSID", "UNITGROUPID", "UNITID", + "DEFAULTLOC", "SPID", "QTYDECIMAL", "SECINV", "MTYPE", "MVER", + "MATERIAL", "FIRM", "FIRMORDER", "FACEDEAL", "SHAPESIZE", + "TECHDESC", "UNITWEIGHT", "MCODE", "MCAT", "COSTITEM", + "ORDERRECTOR", "POHIGHPRICE", "POHGHPRCMNYTYPE", "WWHGHPRC", + "WWHGHPRCMNYTYPE", "SOLOWPRC", "SOLOWPRCMNYTYPE", "TRACK", + "PRICEDECIMAL", "ACCTID", "SALEACCTID", "COSTACCTID", + "DEFAULTROUTINGID", "DEFAULTWORKTYPEID", "PRODUCTPRINCIPAL", + "PLANNER", "ISBACKFLUSH", "MRPCON", "MRPORDER", "CHARTNUMBER", + "INSPECTIONLEVEL", "PROCHKMDE", "WWCHKMDE", "SOCHKMDE", + "WTHDRWCHKMDE", "STKCHKMDE", "OTHERCHKMDE", "INSPECTIONPROJECT", + "NAMEEN", "MODELEN", "HSNUMBER", "IMPOSTTAXRATE", + "CONSUMETAXRATE", "STARTSERVICE", "MAKEFILE", "ISFIX", + "TTERMOFSERVICE", "TTERMOFUSEFULTIME", "PRODUCTNO", "PARAMVALUE", + "MEMO", "CDATE", "CUSERID", "CSTAFFNAME", "MDATE", "MUSERID", + "MSTAFFNAME", "K_ID", "K_PID", "DDATE", "DUSERID", "DSTAFFNAME", + "K_CODE", "AMODEL", "K_COSTITEM", "DWGSYMDESC", "SPDESC", + "CDRATE", "GWEIGHT", "NWEIGHT", "L", "W", "H", "MSIZE", + "K_COST", "BATCHAPPLICANT", "MATSLUGGISHSTATUS", + "SLUGGISHEFFECTIVEDATE", "K_AUTO_ID", "FRATE", "K3CLOUDID" + ], + "postSql": ["select count(1) from t_base_material"], + "preSql": [], + "flushInterval":30000, + "connection": [ + { + "jdbcUrl": "jdbc:mysql://192.168.6.35:9030/erp", + "selectedDatabase": "erp", + "table": ["t_base_material"] + } + ], + "loadProps": { + "format": "json", + "strip_outer_array":"true", + "line_delimiter": "\\x02" + } + } + } + } + ] + } +} + +``` + +执行任务 + +```bash +cd datax +python ./bin/datax.py mysql_2_doris_t_base_material.json +``` + diff --git a/source/_posts/大模型.md b/source/_posts/大模型.md new file mode 100644 index 0000000..d8d8c43 --- /dev/null +++ b/source/_posts/大模型.md @@ -0,0 +1,1384 @@ +--- +title: 大模型 +date: 2025-02-18 10:06:57 +tags: +--- + +# Ollama + +## 1. 安装 + +首先需要下载并安装Ollama,这是运行模型的基础环境。 + + + +![image-20250218102658870](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20250218102658870.png) + +## 2. 下载模型 + +打开命令行终端,根据需要运行以下命令之一来下载对应版本的模型: + +![image-20250218104847668](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20250218104847668.png) + +以DeepSeek为例: + +7B 版本(推荐显存 8G): + +```bash +ollama pull deepseek-coder:7b +``` + +8B 版本(推荐显存 8G): + +```bash +ollama run huihui_ai/deepseek-r1-abliterated:8b +``` + +14B 版本(推荐显存 12G): + +```BASH +ollama run huihui_ai/deepseek-r1-abliterated:14b +``` + +32B 版本(推荐显存 32G): + +```bash +ollama run huihui_ai/deepseek-r1-abliterated:32b +``` + +70B 版本(需要高端显卡支持): + +```bash +ollama run huihui_ai/deepseek-r1-abliterated:70b +``` + +## 3. Ollama 常用命令 + +在使用 Ollama 时,以下是一些常用的命令操作: + +```bash +# 启动 Ollama 服务 +ollama serve + +# 从 Modelfile 创建模型 +ollama create <模型名称> + +# 显示模型信息 +ollama show <模型名称> + +# 运行模型 +ollama run <模型名称> + +# 停止运行中的模型 +ollama stop <模型名称> + +# 从仓库拉取模型 +ollama pull <模型名称> + +# 推送模型到仓库 +ollama push <模型名称> + +# 列出所有已安装的模型 +ollama list + +# 列出正在运行的模型 +ollama ps + +# 复制模型 +ollama cp <源模型> <目标模型> + +# 删除模型 +ollama rm <模型名称> + +# 显示模型文件 +ollama show --modelfile <模型名称> +``` + +## 4. Ollama模型存储目录 + +- macOS: `~/.ollama/models` +- Linux: `/usr/share/ollama/.ollama/models` +- Windows: `C:\Users\%username%\.ollama\models` + +### 如何将它们设置为不同的位置? + +如果需要使用不同的目录,可以将环境变量 `OLLAMA_MODELS` 设置为你选择的目录。 + +> 注意:在 Linux 上使用标准安装程序时,`ollama` 用户需要对指定目录有读写权限。要将目录分配给 `ollama` 用户,请运行 `sudo chown -R ollama:ollama <directory>`. + +请参考[上面的部分](https://ollama.readthedocs.io/faq/#how-do-i-configure-ollama-server)了解如何在你的平台上设置环境变量。 + +![image-20250218132430850](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20250218132430850.png) + +## 5. WSL中Ollama使用Windows中的 + +```shell +# 编辑环境变量 +vim /etc/profile + +# 文件末尾添加 +export PATH="$PATH:/mnt/c/Program Files/Ollama" +alias ollama='ollama.exe' +``` + +# nvidia + +## cuda-toolkit + + + +### 下载 **CUDA Toolkit Installer** + +#### AlmaLinux + +```shell +wget https://developer.download.nvidia.com/compute/cuda/12.8.1/local_installers/cuda-repo-rhel9-12-8-local-12.8.1_570.124.06-1.x86_64.rpm +sudo rpm -i cuda-repo-rhel9-12-8-local-12.8.1_570.124.06-1.x86_64.rpm +sudo dnf clean all +sudo dnf -y install cuda-toolkit-12-8 +``` + +#### Ubuntu + +```shell +wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2404/x86_64/cuda-ubuntu2404.pin +sudo mv cuda-ubuntu2404.pin /etc/apt/preferences.d/cuda-repository-pin-600 +wget https://developer.download.nvidia.com/compute/cuda/12.9.1/local_installers/cuda-repo-ubuntu2404-12-9-local_12.9.1-575.57.08-1_amd64.deb +sudo dpkg -i cuda-repo-ubuntu2404-12-9-local_12.9.1-575.57.08-1_amd64.deb +sudo cp /var/cuda-repo-ubuntu2404-12-9-local/cuda-*-keyring.gpg /usr/share/keyrings/ +sudo apt-get update +sudo apt-get -y install cuda-toolkit-12-9 +``` + +### 安装 **Driver Installer** + +#### 专有驱动 + +```shell +sudo apt-get install -y cuda-drivers +``` + +安装好后,需重启系统才可应用上。 + +```shell +sudo reboot +``` + +### 配置环境变量 + +```shell +vim ~/.bashrc + +# 文件末尾添加 +# cuda 10.2 +export CUDA_HOME=/usr/local/cuda +export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/cuda/lib64:/usr/local/cuda/extras/CUPTI/lib64 +export PATH=$PATH:$CUDA_HOME/bin +``` + +## NVIDIA Container Toolkit + +### 下载 + +#### With `apt`: Ubuntu, Debian + +国内镜像源安装: + +```shell +curl -fsSL https://mirrors.ustc.edu.cn/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg + +curl -s -L https://mirrors.ustc.edu.cn/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \ +sed 's#deb https://nvidia.github.io#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://mirrors.ustc.edu.cn#g' | \ +sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list + +sudo apt-get update + +sudo apt-get install -y nvidia-container-toolkit + +nvidia-container-cli --version + +sudo systemctl restart docker +``` + + + +## nvidia-smi + +> nvidia-smi是nvidia 的系统管理界面 ,其中smi是System management interface的缩写,它可以收集各种级别的信息,查看显存使用情况。此外, 可以启用和禁用 GPU 配置选项 (如 ECC 内存功能)。 + +```shell +nvidia-smi + ++-----------------------------------------------------------------------------------------+ +| NVIDIA-SMI 570.86.09 Driver Version: 571.96 CUDA Version: 12.8 | +|-----------------------------------------+------------------------+----------------------+ +| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC | +| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. | +| | | MIG M. | +|=========================================+========================+======================| +| 0 NVIDIA RTX 4000 Ada Gene... On | 00000000:01:00.0 Off | Off | +| N/A 50C P8 7W / 85W | 4970MiB / 12282MiB | 0% Default | +| | | N/A | ++-----------------------------------------+------------------------+----------------------+ + ++-----------------------------------------------------------------------------------------+ +| Processes: | +| GPU GI CI PID Type Process name GPU Memory | +| ID ID Usage | +|=========================================================================================| +| 0 N/A N/A 16221 C /python3.12 N/A | ++-----------------------------------------------------------------------------------------+ + +``` + +解释相关参数含义: + +GPU:本机中的GPU编号 + +Name:GPU 类型 + +Persistence-M: + +Fan:风扇转速 + +Temp:温度,单位摄氏度 + +Perf:表征性能状态,从P0到P12,P0表示最大性能,P12表示状态最小性能 + +Pwr:Usage/Cap:能耗表示 + +Bus-Id:涉及GPU总线的相关信息; + +Disp.A:Display Active,表示GPU的显示是否初始化 + +Memory-Usage:显存使用率 + +Volatile GPU-Util:浮动的GPU利用率 + +Uncorr. ECC:关于ECC的东西 + +Compute M.:计算模式 + +Processes 显示每块GPU上每个进程所使用的显存情况。 + +### 持续监控 + +```shell +# 使用 watch 命令,它可以定时执行指定的命令并刷新输出。例如,每隔 1 秒刷新一次 GPU 状态,可以使用以下命令 +watch -n 1 nvidia-smi +``` + +## nvidia-smi -L + +```shell +# 列出所有可用的 NVIDIA 设备 +nvidia-smi -L +GPU 0: NVIDIA RTX 4000 Ada Generation Laptop GPU (UUID: GPU-9856f99a-c32c-fe63-b2ad-7bdee2b12291) +``` + +# ModelScope + +## 模型下载 + +### 安装 + +```shell +pip install modelscope +``` + +### 命令行下载 + +```shell +# 下载完整模型库 +modelscope download --model deepseek-ai/DeepSeek-R1-Distill-Qwen-7B +# 下载单个文件到指定本地文件夹(以下载README.md到当前路径下“dir”目录为例) +modelscope download --model deepseek-ai/DeepSeek-R1-Distill-Qwen-7B README.md --local_dir ./dir +``` + +#### 指定下载单个文件(以'tokenizer.json'文件为例) + +``` +modelscope download --model 'Qwen/Qwen2-7b' tokenizer.json +``` + +#### 指定下载多个个文件 + +``` +modelscope download --model 'Qwen/Qwen2-7b' tokenizer.json config.json +``` + +#### 指定下载某些文件 + +``` +modelscope download --model 'Qwen/Qwen2-7b' --include '*.safetensors' +``` + +#### 过滤指定文件 + +``` +modelscope download --model 'Qwen/Qwen2-7b' --exclude '*.safetensors' +``` + +#### 指定下载cache_dir + +``` +modelscope download --model 'Qwen/Qwen2-7b' --include '*.json' --cache_dir './cache_dir' +``` + +模型文件将被下载到`'cache_dir/Qwen/Qwen2-7b'`。 + +#### 指定下载local_dir + +``` +modelscope download --model 'Qwen/Qwen2-7b' --include '*.json' --local_dir './local_dir' +``` + +模型文件将被下载到`'./local_dir'`。 + +如果`cache_dir`和`local_dir`参数同时被指定,`local_dir`优先级高,`cache_dir`将被忽略。 + +### 环境变量配置 + +#### 默认下载目录 + +指定`modelscope`默认下载目录为`/AI/modelscope/hub` + +```bash +echo 'export MODELSCOPE_CACHE=/AI/modelscope/hub' >> ~/.bashrc +``` + +# hugging face + +## 模型下载 + +### 安装 + +`conda` 创建虚拟环境 + +```bash +conda create -n hf-mirror python=3.12 +``` + +`conda` 激活虚拟环境 + +```bash +conda activate hf-mirror +``` + +安装包 + +```bash +pip install -U "huggingface_hub" +``` + +### 命令行下载 + +### Download a single file + +```bash +hf download cyankiwi/Nemotron-Orchestrator-8B-AWQ-8bit --local-dir /AI/hf/models/cyankiwi/Nemotron-Orchestrator-8B-AWQ-8bit +``` + +- + +# Code-forge + +![在这里插入图片描述](https://rustfs.wenyongdalucky.club:443/hexo/cf0364a3ad694f1d8ac6ba97e005c238.png) + +**先说结论(适用于企业/商用环境)** + +- **Anaconda(defaults 源)不能免费商用**:在公司/商业场景下使用 Anaconda 的默认源通常需要付费许可。 +- **Miniconda + conda-forge 能用,但要彻底移除 defaults,并避免在 base 开发**:否则仍可能触发 Anaconda 许可风险。 +- **最稳妥方案:Miniforge + conda-forge**:安装即默认使用 conda-forge,规避 defaults,合规且轻量。 + +> 简单理解:不是“conda 这款工具”收费,而是“使用 Anaconda/Defaults 源的二进制分发”涉及商业许可;改用 **conda-forge** 社区源即可规避许可问题。 + +## 彻底卸载现有 Anaconda/Miniconda + +> 目标:清掉默认源、配置与 shell 残留,避免后续“误连 defaults”。 + +1. 查看 conda 安装路径(含各环境) + + ```bash + conda info --base + ``` + +2. 删除用户侧配置与缓存 + + ```bash + rm -rf ~/.condarc ~/.conda ~/.continuum + ``` + +3. 清理 shell 初始化残留 + + 在 `~/.bashrc`、`~/.bash_profile`、`~/.zshrc` 中搜索并删除与 `conda init`、`anaconda` 相关的初始化片段,保存后重新加载: + + ```bash + source ~/.zshrc # 或 source ~/.bashrc + ``` + +1. Windows 清理(如之前安装过 Anaconda/Miniconda) + + - 通过“应用和功能”卸载 Anaconda/Miniconda。 + - 删除用户目录:`C:\\Users\\<你的用户名>\\.conda`、`C:\\Users\\<你的用户名>\\.continuum`、`C:\\Users\\<你的用户名>\\.condarc`(若存在)。 + - 检查“环境变量”中与 Anaconda/Miniconda 相关的 Path 条目并移除。 + +## 安装 Miniforge + +> Miniforge 默认只使用 conda-forge,装好即合规、可商用。 + +- 发布页(选择你的平台与架构): + `https://github.com/conda-forge/miniforge/releases` + +### macOS/Linux检查 + +```bash +conda list --show-channel-urls | grep -v "^#" | awk '{print $4}' | sort | uniq -c +``` + +![image-20251202155105034](https://rustfs.wenyongdalucky.club:443/hexo/image-20251202155105034.png) + +### Windows检查 + +```powershell +conda list --show-channel-urls | Where-Object { +$_ -notmatch '^#' +} | ForEach-Object { +($_ -split '\s+')[3] +} | Sort-Object | Group-Object | Select-Object Count,Name +``` + +![image-20251202155140771](https://rustfs.wenyongdalucky.club:443/hexo/image-20251202155140771.png) + +到这里,「Miniforge」+「conda-forge」的安装就`已经完成了`!因为「Miniforge」`默认的就是「conda-forge」源`。 + +## 将通道固定为 conda-forge(严格优先) + +> 如果你此前用过 Miniconda,请务必执行本小节以彻底规避 defaults。 + +```bash +# 移除 defaults(如不存在会提示,无妨) +conda config --remove channels defaults || true +# 仅使用 conda-forge,并设置严格优先级 +conda config --add channels conda-forge +conda config --set channel_priority strict +# 查看当前配置 +conda config --show | sed -n '/channels:/,/^$/p' +``` + +建议:不要在 `base` 环境中开发,创建独立环境更干净: + +```bash +conda create -n ai python=3.11 -y +conda activate ai +``` + +## 验证是否仍引用 Anaconda 源 + +> 下面的命令会统计当前环境中每个包来自哪个 channel,方便确认是否“纯 conda-forge”。 + +macOS/Linux: + +```bash +conda list --show-channel-urls | grep -v "^#" | awk '{print $4}' | sort | uniq -c +``` + +Windows: + +```powershell +conda list --show-channel-urls | Where-Object { +$_ -notmatch '^#' +} | ForEach-Object { +($_ -split '\s+')[3] +} | Sort-Object | Group-Object | Select-Object Count,Name +``` + +若输出仅包含 `conda-forge`,说明你的环境是合规的;若还出现 `defaults`/`anaconda`,请返回第三节重新清理并创建新环境。 + +## 常见问题(FAQ) + +- Q:Miniconda + conda-forge 能不能商用? + A:可以,但务必移除 `defaults`,并新建环境后再安装依赖。历史 `base` 或旧环境中若残留来自 `defaults` 的包,仍可能存在许可风险。更稳妥的做法是直接使用 **Miniforge**。 + +- Q:为什么不在 base 环境开发? + A:base 常被用于初始化与管理,历史上最容易“混入” defaults 包。独立环境可保证依赖与渠道的可控性与可复现性。 + +- Q:国内下载慢怎么办? + A:可配置 conda-forge 的国内镜像(如清华镜像站),并保持 `channel_priority strict`,避免回落到 defaults。 + +- Q:已有项目如何迁移? + A:导出依赖、切换通道、重建环境: + + ```bash + conda env export --from-history > env.yml + # 按第三节配置通道为 conda-forge 后 + conda env create -n ai-new -f env.yml + conda activate ai-new + ``` + +## 参考命令清单(便捷复用) + +```bash +# 卸载相关(macOS/Linux) +conda info --base +rm -rf ~/.condarc ~/.conda ~/.continuum +# Miniforge 安装(示例) +curl -LO https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-MacOSX-arm64.sh +bash Miniforge3-MacOSX-arm64.sh -b -p $HOME/miniforge3 +source "$HOME/miniforge3/bin/activate" && conda init zsh && +exec $SHELL +# 仅使用 conda-forge(严格优先) +conda config --remove channels defaults || true +conda config --add channels conda-forge +conda config --set channel_priority strict +# 创建与使用环境 +conda create -n ai python=3.11 -y +conda activate ai +``` + +# Anaconda + +> + +## 安装 + +### minicoda + +[安装 Miniconda — Anaconda 文档](https://docs.anaconda.net.cn/miniconda/install/) + +```shell +wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh +bash ~/Miniconda3-latest-Linux-x86_64.sh +source ~/.zshrc +``` + +## 常用命令 + +### 管理环境 + +1. 列出所有的环境 + ```shell + conda env list + ``` + + + +2. 查看conda下的包 + ```she + conda list + ``` + +3. 创建环境 + ```shell + conda create -n env-name [list of package] + ``` + + -n env-name 是设置新建环境的名字,list of package 是可选项,选择要为该环境安装的包 + 如果我们没有指定安装python的版本,conda会安装我们最初安装conda所装的那个版本的python + 若创建特定python版本的包环境,需键入 + + ```shell + conda create -n env-name python=3.6 + +4. 激活环境 + ```shell + conda activate env-name + ``` + + 切换到base环境 + ```shell + conda activate base + ``` + +5. 删除环境 + 执行以下命令可以将该指定虚拟环境及其中所安装的包都删除。 + + ```shell + conda remove --name env_name --all + ``` + + 如果只删除虚拟环境中的某个或者某些包则是: + ```shell + conda remove --name env_name package_name + ``` + + + + +## 问题 + +1. conda激活[虚拟环境](https://so.csdn.net/so/search?q=虚拟环境&spm=1001.2101.3001.7020),只显示环境名称,不再显示用户名和当前文件夹 + + ```shell + #在个人环境下修改 + conda activate gatkenv + + conda env config vars set PS1='($CONDA_DEFAULT_ENV)[\u@\h \W]$' + #重启环境就ok了 + + conda deactivate + + conda activate gatkenv + + #在所有的虚拟环境下修改,这个命令的意思是在~/.condarc下添加一行 + + conda config --set env_prompt "({default_env})[\u@\h \W]$" + + #取消设置 + + conda config --remove-key env_prompt + ``` + + + +2. conda 安装后没有将conda置为默认 + + ```shell + Do you wish to update your shell profile to automatically initialize conda? + This will activate conda on startup and change the command prompt when activated. + If you'd prefer that conda's base environment not be activated on startup, + run the following command when conda is activated: + + conda config --set auto_activate_base false + + You can undo this by running `conda init --reverse $SHELL`? [yes|no] + [no] >>> + + You have chosen to not have conda modify your shell scripts at all. + To activate conda's base environment in your current shell session: + + eval "$(/home/user/miniconda3/bin/conda shell.YOUR_SHELL_NAME hook)" + + To install conda's shell functions for easier access, first activate, then: + + conda init + + Thank you for installing Miniconda3! + + ``` + + 则执行以下shell + + ```shell + # eval "$(/home/user/miniconda3/bin/conda shell.SHELL_NAME hook)" + # bash 下 + eval "$(/home/user/miniconda3/bin/conda shell.bash hook)" + conda init + # zsh 下 + eval "$(/home/user/miniconda3/bin/conda shell.zsh hook)" + conda init + ``` + +3. conda和pip换镜像源 + + 修改conda,通过以下命令进行添加 + + ```lua + conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ + conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/ + conda config --set show_channel_urls yes + ``` + + 修改后可以通过以下命令查看: + + ```armasm + conda info + ``` + + 下载试试:如果两都能看到下载源,那么就更改成功。 + + ```mipsasm + conda install + ``` + + ##### Linux系统下: + + linux系统下要到用户的文件夹下新建目录.pip,并在目录新建配置文件pip.conf + + ```bash + mkdir ~/.pip + cd ~/.pip + vi pip.conf + ``` + + 编辑内容: + + ```ini + [global] + index-url=https://pypi.tuna.tsinghua.edu.cn/simple + [install] + trusted-host=pypi.tuna.tsinghua.edu.cn + disable-pip-version-check = true + timeout = 6000 + ``` + + 下载试试: + + ```bash + pip install + ``` + +# Jupyter Notebook + +> + +## 全局 + +### 安装 + +```shell +pip install jupyter +``` + +### 运行 + +```shell +jupyter notebook +# 若是root用户执行,会出现警告 Running as root is not recommended. Use --allow-root to bypass. 需在后面加上 --allow-root +jupyter notebook --allow-root +``` + +## Conda 虚拟环境 + +### 安装 + +#### 使用 nb_conda_kernels 添加所有环境 + +```bash +conda activate my-conda-env # this is the environment for your project and code +conda install ipykernel +conda deactivate + +conda activate base # could be also some other environment +conda install nb_conda_kernels +jupyter notebook +``` + +#### 配置密码(适用于 Jupyter 7+ / JupyterLab 4+) + +在 安装有 jupyter 包的终端中运行 + +```bash +jupyter server password +``` + +然后按提示输入并确认密码。 +Jupyter 会自动将加密后的密码写入配置文件(通常是 `~/.jupyter/jupyter_server_config.json`)。 + +#### 生成并编辑配置文件 + +**生成配置文件**(如果还没有): + +```bash +jupyter notebook --generate-config +``` + +(对于新版本): + +```bash +jupyter server --generate-config +``` + +**编辑配置文件**(通常是 `~/.jupyter/jupyter_server_config.py` 或 `jupyter_notebook_config.py`) + +```python +c = get_config() + +# 允许远程访问 +c.ServerApp.ip = '0.0.0.0' # 注意:新版本用 ServerApp 而非 NotebookApp + +# 禁用自动打开浏览器 +c.ServerApp.open_browser = False + +# 设置端口(可选) +c.ServerApp.port = 8888 + +# (可选)设置工作目录 +c.ServerApp.root_dir = '/path/to/your/notebooks' + +# (可选)允许通过 token 或密码登录(设了密码后 token 会失效) +c.ServerApp.token = '' # 禁用 token(如果已设密码) +c.ServerApp.password_required = True +``` + +> 🔔注意:**Jupyter 新版本中 `NotebookApp` 已被 `ServerApp` 取代**,所以配置项要使用 `c.ServerApp.xxx`。 + +### 启动 Jupyter + +```bash +jupyter notebook --ip=0.0.0.0 --port=5600 --no-browser +``` + +或直接: + +```bash +jupyter lab --ip=0.0.0.0 --port=5600 --no-browser +``` + +### 注册服务 + +#### 创建 systemd 服务文件 + +创建服务文件(以用户 `user` 为例,替换为你的实际用户名): + +```bash +sudo vim /etc/systemd/system/jupyter-lab.service +``` + +内容如下(**关键:使用绝对路径 + conda run**): + +```ini +[Unit] +Description=Jupyter Lab Service +After=network.target + +[Service] +Type=simple +User=user +WorkingDirectory=/home/user +Environment=PATH=/home/user/miniforge3/envs/jupyter-env/bin:/home/user/miniforge3/condabin:/usr/local/bin:/usr/bin:/bin +ExecStart=/home/user/miniforge3/envs/jupyter-env/bin/jupyter lab --ip=0.0.0.0 --port=7745 --no-browser +Restart=on-failure +RestartSec=5 +StandardOutput=journal +StandardError=journal + +[Install] +WantedBy=multi-user.target +``` + +#### 重载 systemd 并启用服务 + +```bash +# 重载 systemd 配置 +sudo systemctl daemon-reload + +# 启动服务 +sudo systemctl start jupyter-lab + +# 设置开机自启 +sudo systemctl enable jupyter-lab + +# 查看状态和日志 +sudo systemctl status jupyter-lab +journalctl -u jupyter-lab -f +``` + + + +# UV + +**优点:** + +- 极快的速度(比 pip 快 10-100 倍) +- 自动管理 Python 版本 +- 内置虚拟环境管理 +- 现代化工具,Rust 编写 +- 无需单独安装 Python + +## 安装 + +```bash +curl -LsSf https://astral.sh/uv/install.sh | sh +``` + +## 创建项目并自动安装 Python + +```bash +uv init myproject +cd myproject +uv python install 3.12 # 自动下载安装 Python 3.12 +uv venv # 创建虚拟环境 +source .venv/bin/activate +``` + +## 安装包(使用 pyproject.toml) + +```bash +# 创建项目 +uv init langchain-project +cd langchain-project + +# 添加依赖 +uv add langchain==1.0.0 +uv add langchain-core==1.0.0 +uv add langchain-community==1.0.0 +uv add langchain-openai # 可选 +``` + + + +# UnSloth + +# vLLM + +## 安装 + +```shell +conda create -n vLLM python=3.12 +conda activate vLLM +pip install vLLM + +# FlashInfer is optional but required for specific functionalities such as sliding window attention with Gemma 2. +# For CUDA 12.4 & torch 2.4 to support sliding window attention for gemma 2 and llama 3.1 style rope +pip install flashinfer -i https://flashinfer.ai/whl/cu124/torch2.4 +# For other CUDA & torch versions, please check https://docs.flashinfer.ai/installation.html +# 也可下载到本地 +wget https://github.com/flashinfer-ai/flashinfer/releases/download/v0.2.5/flashinfer_python-0.2.5+cu124torch2.6-cp38-abi3-linux_x86_64.whl#sha256=43d767b912c0c43a04be99595e0123eab9385fc72530a2874b5fb08e3145c0be +pip install flashinfer_python-0.2.5+cu124torch2.6-cp38-abi3-linux_x86_64.whl --no-deps +``` + +## 部署 + +### deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B + +```shell +CUDA_VISIBLE_DEVICES=0 vllm serve /mnt/e/modelscope/deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B --port 8102 --max-model-len 2048 --api-key token-abc123 +``` + +- CUDA_VISIBLE_DEVICES=0: 指定使用的 GPU 设备 ID。 0 表示使用第一块 GPU。如果您有多块 GPU,可以根据需要修改为其他 ID (例如 CUDA_VISIBLE_DEVICES=1,2 使用 GPU 1 和 GPU 2)。如果您只有一块 GPU,通常使用 0 即可。 +- `/mnt/e/modelscope/deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B`: **模型路径。** 请替换为您在步骤 2 中模型实际保存的路径。 +- `--port 8102`: **服务端口号。** `8102` 是服务启动后监听的端口。您可以根据需要修改端口号,例如 `--port 8000`。在后续代码调用中,需要使用相同的端口号。 +- --max-model-len 16384: 模型最大上下文长度。 16384 表示模型处理的最大输入序列长度。您可以根据您的 GPU 显存大小和需求调整此参数。对于 /mnt/e/modelscope/deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B 模型,16384 是一个较大的上下文长度。您可以尝试减小此值以减少显存占用,例如 --max-model-len 2048 或更小。 +- **执行启动命令:** 在终端或命令提示符中执行上述 `vllm serve` 命令。 +- **注意 GPU 显存:** 启动 vLLM 服务会占用 GPU 显存。请确保您的 GPU 显存足够运行模型。如果显存不足,可能会导致启动失败或运行缓慢。您可以尝试减小 `--max-model-len` 参数或使用更小规模的模型。 + +### Qwen/Qwen3-4B + +```shell +vllm serve ~/modelscope/Qwen/Qwen3-4B --api-key token-abc123 --enable-reasoning --reasoning-parser deepseek_r1 --max_model_len=2048 --gpu_memory_utilization=0.85 +``` + +#### supervisor + +```conf +sudo vim vLLM-Qwen-Qwen3-4B.conf +[program:vLLM-Qwen-Qwen3-4B.conf] +command=zsh -c "source /home/user/miniconda3/bin/activate && source activate vLLM && vllm serve ~/modelscope/Qwen/Qwen3-4B --api-key token-abc123 --enable-reasoning --reasoning-parser deepseek_r1 --max_model_len=2048 --gpu_memory_utilization=0.85" +user=user +autostart=true +autorestart=true +stderr_logfile=/var/log/supervisor/vLLM-Qwen-Qwen3-4B/err.log +stdout_logfile=/var/log/supervisor/vLLM-Qwen-Qwen3-4B/out.log +stopasgroup=true +``` + +#### Docker + +Docker Compose方式 + +```yaml +services: + vllm: + image: 'vllm/vllm-openai:latest' + container_name: vllm-Qwen-Qwen3-8B-AWQ + restart: always + environment: + - 'TZ:Asia/Shanghai' + - 'VLLM_USE_MODELSCOPE:True' + - 'HF_HUB_OFFLINE:1' + ports: + - '8000:8000' + volumes: + - /home/user/modelscope:/root/.cache/modelscope + - /etc/localtime:/etc/localtime:ro + # 启用 NVIDIA GPU 支持 + # 在 Docker Desktop 上,可能只需要 'gpu', + # 在 Linux 服务器上,通常是 'nvidia' + # 你提供的命令行是 --runtime nvidia --gpus all + # Docker Compose 对应的方式是: + deploy: + resources: + reservations: + devices: + - driver: nvidia + count: all # 或者指定具体的GPU数量,如 1, 2 等 + capabilities: [gpu] # 确保指定 GPU 能力 + # IPC 模式 + # ipc: host 允许容器与宿主机共享 IPC 命名空间, + # 这在某些高性能场景下有用,例如共享内存。 + ipc: host + # 容器启动时执行的命令和参数 + command: + - --model + - /root/.cache/modelscope/Qwen/Qwen3-8B-AWQ + - --served_model_name + - Qwen/Qwen3-4B # 你希望这个model暴露时的名称,如果不填默认为本地模型权重路径 + - --max_model_len + - "8192" # 支持的最长上下文长度,根据显存大小自行匹配,注意这里需要是字符串 + # --api_key 参数在 vLLM 0.3.x 版本中可能不再是直接的命令行参数, + # 而是通过环境变量 API_KEY 来设置的。 + # 我已将其移到 environment 部分。 + # 如果你的 vLLM 版本仍然支持命令行参数,请取消注释下一行: + # - --api_key + # - "<YOUR-API-KEY-HERE>" + - --gpu_memory_utilization + - "0.9" + - --max-num-seqs + - "128" + - --api-key + - token-abc123 + - --reasoning-parser + - deepseek_r1 +``` + + + +# LLama.cpp + +> `llama.cpp`是一个基于纯`C/C++`实现的高性能大语言模型推理引擎,专为优化本地及云端部署而设计。其核心目标在于通过底层硬件加速和量化技术,实现在多样化硬件平台上的高效推理,同时保持低资源占用与易用性。 + +## 编译llama.cpp + +首先从`Github`上下载`llama.cpp`的源码: + +```text +git clone https://github.com/ggml-org/llama.cpp +cd llama.cpp +``` + +`llama.cpp`支持多种硬件平台,可根据实际的硬件配置情况选择合适的编译参数进行编译,具体可以参考文档`docs/build.md`。 + +**安装CMAKE** + +```shell +wget https://github.com/Kitware/CMake/releases/download/v4.0.0-rc4/cmake-4.0.0-rc4-linux-x86_64.sh +chmod -R 777 cmake-4.0.0-rc4-linux-x86_64.sh +./cmake-4.0.0-rc4-linux-x86_64.sh +mv cmake-4.0.0-rc4-linux-x86_64/ /usr/local/cmake +echo 'export PATH="/usr/local/cmake/bin:$PATH"' >> ~/.bashrc +source ~/.bashrc + +sudo dnf install gcc-toolset-13-gcc gcc-toolset-13-gcc-c++ +source /opt/rh/gcc-toolset-13/enable +``` + +**编译CPU版本** + +```text +cmake -B build +cmake --build build --config Release -j 8 +``` + +**编译GPU版本** + +编译英伟达`GPU`版本需要先装好驱动和`CUDA`,然后执行下面的命令进行编译 + +```text +cmake -B build -DGGML_CUDA=ON -DGGML_CUDA_ENABLE_UNIFIED_MEMORY=1 +cmake --build build --config Release -j 8 +``` + +> 编译完成后,可执行文件和库文件被存放在`build/bin`目录下。 + +## 模型下载与转换 + +首先从魔搭社区下载模型: + +```text +pip install modelscope +modelscope download --model deepseek-ai/DeepSeek-R1-Distill-Qwen-7B --local_dir DeepSeek-R1-Distill-Qwen-7B +``` + +下载好的模型是以`HuggingFace`的`safetensors`格式存放的,而`llama.cpp`使用的是`GGUF`格式,因此需要先要把模型转换为`GGUF`格式: + +```text +# 安装python依赖库 +pip install -r requirements.txt +# 转换模型 +python convert_hf_to_gguf.py DeepSeek-R1-Distill-Qwen-7B/ +``` + +转换成功后,在该目录下会生成一个`FP16`精度、`GGUF`格式的模型文件`DeepSeek-R1-Distill-Qwen-7B-F16.gguf`。 + +## 模型量化 + +`FP16`精度的模型跑起来可能会有点慢,我们可以对模型进行量化以提升推理速度。 + +`llama.cpp`主要采用了分块量化(`Block-wise Quantization`)和`K-Quantization`算法来实现模型压缩与加速,其核心策略包括以下关键技术: + +1. **分块量化(Block-wise Quantization)** + 该方法将权重矩阵划分为固定大小的子块(如`32`或`64`元素为一组),每个子块独立进行量化。通过为每个子块分配独立的缩放因子(`Scale`)和零点(`Zero Point`),有效减少量化误差。例如,`Q4_K_M`表示每个权重用`4`比特存储,且子块内采用动态范围调整。 +2. **K-Quantization(混合精度量化)** + 在子块内部进一步划分更小的单元(称为“超块”),根据数值分布动态选择量化参数。例如,`Q4_K_M`将超块拆分为多个子单元,每个子单元使用不同位数的缩放因子(如`6bit`的缩放因子和`4bit`的量化值),通过混合精度平衡精度与压缩率。 +3. **重要性矩阵(Imatrix)优化** + 通过分析模型推理过程中各层激活值的重要性,动态调整量化策略。高重要性区域保留更高精度(如`FP16`),低重要性区域采用激进量化(如`Q2_K`),从而在整体模型性能损失可控的前提下实现高效压缩。 +4. **量化类型分级策略** + 提供`Q2_K`至`Q8_K`等多种量化级别,其中字母后缀(如`_M`、`_S`)表示优化级别: +5. **Q4_K_M**:中等优化级别,平衡推理速度与精度(常用推荐)。 +6. **Q5_K_S**:轻量化级别,侧重减少内存占用 + +典型场景下,`Q4_K_M`相比`FP16`模型可减少`70%`内存占用,推理速度提升`2-3`倍,同时保持`95%`以上的原始模型精度。实际部署时需根据硬件资源(如`GPU`显存容量)和任务需求(如生成文本长度)选择量化策略。 + +执行下面的命令可将`FP16`精度的模型采用`Q4_K_M`的量化策略进行量化: + +```text +./build/bin/llama-quantize DeepSeek-R1-Distill-Qwen-7B/DeepSeek-R1-Distill-Qwen-7B-F16.gguf DeepSeek-R1-Distill-Qwen-7B/DeepSeek-R1-Distill-Qwen-7B-Q4_K_M.gguf Q4_K_M +``` + +量化完成后,模型文件由`15.2G`减少到`4.7G`。 + +## 运行模型 + +模型量化完后,我们就可以运行模型来试试效果了。`llama.cpp`提供了多种运行模型的方式: + +### 命令行方式 + +执行下面的命令就可以在命令行与模型进行对话了: + +```text +./build/bin/llama-cli -m DeepSeek-R1-Distill-Qwen-7B/DeepSeek-R1-Distill-Qwen-7B-Q4_K_M.gguf -cnv +``` + +### HTTP Server方式 + +由于模型是以`Markdown`格式输出内容,因此用命令行的方式看着不太方便。`llama.cpp`还提供`HTTP Server`的方式运行,交互性要好很多。 + +首先在终端执行命令 + +```text +./build/bin/llama-server -m DeepSeek-R1-Distill-Qwen-7B/DeepSeek-R1-Distill-Qwen-7B-Q4_K_M.gguf --port 8088 +``` + +然后打开浏览器,输入地址`http://127.0.0.1:8088`就可以在网页上与模型进行交互了,非常方便! + +# Ktransformers + +安装 + + + +# LLaMA-Factory + +> 可参考文章:[DeepSeek-R1-7B-Distill模型微调全过程记录,LLaMA_Factory训练自己的数据集,合并lora微调模型并量化为gguf,接入微信实现自动对话回复_微信_qq_53091149-DeepSeek技术社区](https://deepseek.csdn.net/67b84a893c9cd21f4cb9aab6.html#devmenu2) + +## 安装 + +```shell +# 首先 conda创建环境 +conda create -n LLaMA-Factory python=3.12 +# 激活环境 +conda activate LLaMA-Factory +# 从GitHub上拉去项目代码到当前目录下 +git clone https://github.com/hiyouga/LLaMA-Factory.git +# 进入目录 +cd LLaMA-Factory +# 安装所需依赖 +pip install -e ".[torch,metrics]" +# 启动webui +python src/webui.py +``` + +## 微调 + +![image-20250320152454509](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20250320152454509.png) + +![image-20250320152533756](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20250320152533756.png) + +合并 + +![image-20250320152645802](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20250320152645802.png) + +# AutoAWQ量化 + +## 安装 + +```shell +conda create -n AutoAWQ python=3.12 +conda activate AutoAWQ +pip install torch +pip install autoawq +``` + +## 脚本 + +默认AutoAWQ会从Huggingface上下载数据集mit-han-lab/pile-val-backup,会因为网络问题失败 + +需事先手动下载,通过modelscope + +```shell +modelscope download --dataset mit-han-lab/pile-val-backup --local_dir ~/modelscope/mit-han-lab/pile-val-backup +``` + +以`Qwen/Qwen3-4B`模型为例 + +```shell +from awq import AutoAWQForCausalLM +from transformers import AutoTokenizer +from datasets import load_dataset + +# 加载本地数据集 +def load_calib_data(): + data=load_dataset('/home/user/modelscope/mit-han-lab/pile-val-backup', split="validation") + return [text for text in data["text"] if text.strip() != '' and len(text.split(' ')) > 20] + +model_path = '/home/user/modelscope/Qwen/Qwen3-4B' +quant_path = '/home/user/modelscope/Qwen/Qwen3-4B-awq' +quant_config = { "zero_point": True, "q_group_size": 128, "w_bit": 4, "version": "GEMM" } + + +# Load model +# 加载模型 + + +model = AutoAWQForCausalLM.from_pretrained( + model_path, **{"low_cpu_mem_usage": True, "use_cache": False} +) +tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True) + + +# Quantize +# 量化 + +calib_data=load_calib_data() +model.quantize(tokenizer, quant_config=quant_config, calib_data=calib_data) + + +# Save quantized model +# 保存量化模型 + + +model.save_quantized(quant_path) +tokenizer.save_pretrained(quant_path) + + +print(f'Model is quantized and saved at "{quant_path}"') +``` + + + +# 大模型Dense、MoE 与 Hybrid-MoE 架构的比较 + +在大模型架构设计中,Dense(全连接)、MoE(混合专家)和Hybrid-MoE(混合式MoE)是三种主流的参数组织方式,它们在模型容量、计算效率和应用场景上存在显著差异。以下从核心原理、技术特点、优缺点及适用场景进行系统对比: + +------ + +## **1. 核心原理对比** + +| **架构类型** | **核心思想** | **典型模型** | +| -------------- | ------------------------------------------------------------ | -------------------------- | +| **Dense** | 所有参数对所有输入生效,每层神经元全连接,统一处理所有输入特征。 | GPT-3、BERT、LLAMA | +| **MoE** | 将模型划分为多个“专家”(子网络),每个输入仅激活部分专家,通过路由机制动态分配任务。 | Switch Transformer、GShard | +| **Hybrid-MoE** | 混合Dense和MoE层:部分层全连接,部分层采用MoE结构,平衡计算效率和模型容量。 | DeepSeek-MoE、Google GLaM | + +------ + +## **2. 技术特点与性能对比** + +| **维度** | **Dense** | **MoE** | **Hybrid-MoE** | +| -------------- | --------------------------------------------------- | ------------------------------------------------------------ | ----------------------------------------- | +| **参数规模** | 总参数量=激活参数量,随层数线性增长。 | 总参数量高(专家数×专家规模),但激活参数量低(仅激活部分专家)。 | 介于两者之间,MoE层数可控。 | +| **计算效率** | 计算成本高(FLOPs与参数量正相关),适合小规模模型。 | 相同参数量下,FLOPs显著降低(仅激活部分专家)。 | 通过调整MoE层比例,灵活平衡计算开销。 | +| **训练稳定性** | 收敛稳定,梯度传播路径简单。 | 路由机制易导致专家负载不均衡,需复杂正则化。 | 稳定性优于纯MoE,但仍需路由优化。 | +| **扩展性** | 参数规模受硬件限制,千亿级后成本陡增。 | 可扩展至万亿参数(如GShard-1.6T),适合超大规模模型。 | 通过局部MoE化实现高效扩展,适配中等规模。 | +| **显存占用** | 高(需存储全部参数梯度)。 | 显存需求更高(专家参数独立存储)。 | 显存介于两者之间,取决于MoE层占比。 | +| **应用场景** | 通用任务、资源受限场景。 | 超大规模预训练、多任务学习。 | 需平衡性能与成本的工业级应用。 | + +------ + +## **3. 优缺点对比** + +### **Dense架构** + +- **优点**: + - 结构简单,训练稳定性高。 + - 参数利用率最大化,适合小规模高精度任务。 +- **缺点**: + - 计算成本随参数量指数级增长,难以扩展至超大规模。 + - 显存占用高,限制单卡可训练模型规模。 + +### **MoE架构** + +- **优点**: + - 计算效率高,相同FLOPs下模型容量更大。 + - 支持万亿级参数扩展,适合分布式训练。 +- **缺点**: + - 路由机制复杂,易出现专家“坍缩”(部分专家未被激活)。 + - 显存和通信开销大,需定制化负载均衡策略。 + +### **Hybrid-MoE架构** + +- **优点**: + - 灵活性高,可通过调整MoE层位置平衡性能与成本。 + - 保留关键层的全连接特性,提升任务特定性能。 +- **缺点**: + - 需精心设计MoE层分布,调参成本较高。 + - 仍面临部分MoE的稳定性挑战。 + +------ + +## **4. 典型应用场景** + +| **架构** | **适用场景** | +| -------------- | ------------------------------------------------------------ | +| **Dense** | - 中小规模模型(<100B参数) - 对训练稳定性要求高的任务(如对话生成) - 边缘设备推理 | +| **MoE** | - 超大规模预训练(>500B参数) - 多任务/多模态学习 - 云端高性能计算集群 | +| **Hybrid-MoE** | - 中等规模模型(100B-500B参数) - 需兼顾通用性与效率的工业场景 - 长文本处理任务 | + +------ + +## **5. 技术选型建议** + +- **选择Dense的条件**: + - 资源有限(单卡训练/推理)。 + - 任务单一,无需极高模型容量。 + - 追求极简架构和稳定收敛。 +- **选择MoE的条件**: + - 追求极致模型性能(如AGI探索)。 + - 拥有大规模计算集群(千卡级)。 + - 多任务/多模态需求显著。 +- **选择Hybrid-MoE的条件**: + - 需平衡模型容量与计算成本。 + - 部分任务依赖全连接层的强表征能力(如逻辑推理)。 + - 希望渐进式扩展模型规模。 + +------ + +## **6. 未来发展方向** + +1. **Dense架构优化**: + - 参数高效微调(LoRA、Adapter)。 + - 动态稀疏激活(如微软的DeepSpeed-MoE)。 +2. **MoE架构改进**: + - 更智能的路由机制(如基于强化学习)。 + - 专家共享与分层MoE设计。 +3. **Hybrid-MoE创新**: + - 自动化MoE层分布搜索(NAS技术)。 + - 异构专家设计(不同专家结构适配不同任务)。 + +------ + +## **总结** + +- **Dense**:简单可靠,适合资源受限场景,但扩展性差。 +- **MoE**:计算高效,扩展性强,但工程复杂度高。 +- **Hybrid-MoE**:折中方案,平衡性能与成本,需精细调优。 + +实际选型需结合**任务需求**、**硬件资源**和**工程能力**综合评估。对于大多数企业级应用,Hybrid-MoE可能是当前的最优解,而科研前沿更倾向于探索纯MoE的极限能力。 + +# Dify + +## Dify 向量存储机制简述 + +在 Dify 中,文档数据(如知识库文档、FAQ、手册等)需要经过向量化处理,才能用于语义检索和问答增强。其核心处理流程如下图所示: + +``` +文档(原始文本) + ↓ +Embedding(由 OpenAI, Hugging Face, etc. 提供) + ↓ +向量(高维 float 数组) + ↓ +Vector Store(默认为 Weaviate) + ↓ +Dify 查询时执行语义检索,返回匹配片段 +``` + +向量存储的作用 + +Dify 本身不负责存储嵌入向量,而是通过调用第三方向量数据库来管理这些向量。向量数据库的主要职责包括: + +- 存储文本对应的向量数据(float 数组) +- 为每条向量建立索引,支持高效近似/精确搜索 +- 提供语义检索 API(如 top-k 相似度查找) +- 支持元数据管理(如 document_id, source, tags 等) diff --git a/source/_posts/对象存储.md b/source/_posts/对象存储.md new file mode 100644 index 0000000..efa3fc7 --- /dev/null +++ b/source/_posts/对象存储.md @@ -0,0 +1,87 @@ +--- +title: 对象存储 +date: 2025-07-23 14:27:11 +tags: +--- + +# RustFS + +## 安装 + +### Docker + +新建rustfs目录 + +```shell +mkdir ~/rustfs +cd ~/rustfs +``` + +编辑docker-compose.yml文件 + +```shell +vim docker-compose.yml +``` + +新增以下内容 + +```yaml +services: + rustfs: + image: 'rustfs/rustfs:latest' + container_name: 'rustfs' + restart: always + environment: + - 'TZ:Asia/Shanghai' + - 'RUSTFS_ACCESS_KEY=rustfsadmin' + - 'RUSTFS_SECRET_KEY=rustfsadmin' + - 'RUSTFS_CONSOLE_ENABLE=true' + - 'RUSTFS_TLS_PATH=/certs' + ports: + - '9000:9000' + volumes: + - /etc/localtime:/etc/localtime:ro + - /mnt/rustfs/data:/data + - /usr/ssl:/certs + command: + - "rustfs" + - "--console-enable" + - "--server-domains" + - "rustfs.wenyongdalucky.club" +``` + +:wq保存后退出 + +建立数据挂载目录 + +```shell +sudo mkdir -p /mnt/rustfs/data +``` + +在 `~/rustfs` 目录下使用Docker Compose启动容器 + +```shell +docker compose up -d +``` + +查看日志 + +```shell +docker logs rustfs -f +``` + +使用密钥登录的方式,输入账号、密钥 + +这里查看之前 docker-compose.yml文件中的 RUSTFS_ACCESS_KEY、RUSTFS_SECRET_KEY 进行登录即可。 + +这里就都输入 rustfsadmin登录 + +进入到控制台 + +创建存储桶 hexo + +![image-20250723145626454](https://rustfs.wenyongdalucky.club:443/hexo/image-20250723145626454.png) + +需将桶配置中的访问策略改为 公有,防止无法通过Markdown访问 + +![image-20250723145728392](https://rustfs.wenyongdalucky.club:443/hexo/image-20250723145728392.png) diff --git a/source/_posts/设计模式.md b/source/_posts/设计模式.md new file mode 100644 index 0000000..d3c8adc --- /dev/null +++ b/source/_posts/设计模式.md @@ -0,0 +1,82 @@ +--- +title: 设计模式 +date: 2023-01-03 10:11:47 +author: 文永达 +--- + +# 设计模式 + +## 工厂模式 + +### 概述 + +该模式用来封装和管理类的创建,终极目的是为了解耦,实现创建者和调用者的分离。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 + +工厂模式可以细分为三种: + +**简单工厂模式** + +**工厂方法模式** + +**抽象工厂模式** + +### 传统模式 + +创建不同种类的pizza对象 + +```java + Pizza orderPizza(String type) { + Pizza pizza; + if(type.equals("chess"){ + pizza = new ChessPizza(); + }else if(type.equals("greek"){ + pizza = new GreekPizza(); + }else if(type.equals("apple"){ + pizza = new ApplePizza(); + } + .... + //准备材料 + pizza.prepare(); + //烘烤 + pizza.bake(); + //切 + pizza.cut(); + //装盒 + pizza.box(); + return pizza; + } +``` + + + +### 简单工厂模式 + +在传统模式中直接让调用者去创建对象。但是,假如创建对象的过程比较复杂,我们可以将其交由一个新的类来创建,这种类叫工厂类,调用者只需要调用这个类的函数来创建对象就行了,无需再书写创建对象。 + +```java +public class SimpleFactory { + + public static Pizza createPizza(String pizzaType){ + Pizza pizza = null; + System.out.println("使用了简单工厂模式"); + if (pizzaType.equals("greek")) { + pizza = new GreekPizza(); + pizza.setName("greek"); + } else if (pizzaType.equals("chess")) { + pizza = new ChessPizza(); + pizza.setName("chess"); + } else if (pizzaType.equals("pepper")) {//新增PepperPizza的时候 修改了源代码 违反了ocp原则 如果新增10000个? + //那就很麻烦了 + pizza = new PepperPizza(); + pizza.setName("pepper"); + } + + return pizza; + } +} +``` + + + +### + diff --git a/themes/.gitkeep b/themes/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..8dcba81 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,1831 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@adobe/css-tools@~4.3.1": + version "4.3.3" + resolved "https://registry.npmmirror.com/@adobe/css-tools/-/css-tools-4.3.3.tgz#90749bde8b89cd41764224f5aac29cd4138f75ff" + integrity sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ== + +"@asamuzakjp/css-color@^3.2.0": + version "3.2.0" + resolved "https://registry.npmmirror.com/@asamuzakjp/css-color/-/css-color-3.2.0.tgz#cc42f5b85c593f79f1fa4f25d2b9b321e61d1794" + integrity sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw== + dependencies: + "@csstools/css-calc" "^2.1.3" + "@csstools/css-color-parser" "^3.0.9" + "@csstools/css-parser-algorithms" "^3.0.4" + "@csstools/css-tokenizer" "^3.0.3" + lru-cache "^10.4.3" + +"@babel/helper-string-parser@^7.27.1": + version "7.27.1" + resolved "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687" + integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== + +"@babel/helper-validator-identifier@^7.28.5": + version "7.28.5" + resolved "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4" + integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q== + +"@babel/parser@^7.6.0", "@babel/parser@^7.9.6": + version "7.29.2" + resolved "https://registry.npmmirror.com/@babel/parser/-/parser-7.29.2.tgz#58bd50b9a7951d134988a1ae177a35ef9a703ba1" + integrity sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA== + dependencies: + "@babel/types" "^7.29.0" + +"@babel/types@^7.29.0", "@babel/types@^7.6.1", "@babel/types@^7.9.6": + version "7.29.0" + resolved "https://registry.npmmirror.com/@babel/types/-/types-7.29.0.tgz#9f5b1e838c446e72cf3cd4b918152b8c605e37c7" + integrity sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A== + dependencies: + "@babel/helper-string-parser" "^7.27.1" + "@babel/helper-validator-identifier" "^7.28.5" + +"@csstools/color-helpers@^5.1.0": + version "5.1.0" + resolved "https://registry.npmmirror.com/@csstools/color-helpers/-/color-helpers-5.1.0.tgz#106c54c808cabfd1ab4c602d8505ee584c2996ef" + integrity sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA== + +"@csstools/css-calc@^2.1.3", "@csstools/css-calc@^2.1.4": + version "2.1.4" + resolved "https://registry.npmmirror.com/@csstools/css-calc/-/css-calc-2.1.4.tgz#8473f63e2fcd6e459838dd412401d5948f224c65" + integrity sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ== + +"@csstools/css-color-parser@^3.0.9": + version "3.1.0" + resolved "https://registry.npmmirror.com/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz#4e386af3a99dd36c46fef013cfe4c1c341eed6f0" + integrity sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA== + dependencies: + "@csstools/color-helpers" "^5.1.0" + "@csstools/css-calc" "^2.1.4" + +"@csstools/css-parser-algorithms@^3.0.4": + version "3.0.5" + resolved "https://registry.npmmirror.com/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz#5755370a9a29abaec5515b43c8b3f2cf9c2e3076" + integrity sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ== + +"@csstools/css-tokenizer@^3.0.3": + version "3.0.4" + resolved "https://registry.npmmirror.com/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz#333fedabc3fd1a8e5d0100013731cf19e6a8c5d3" + integrity sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw== + +"@types/trusted-types@^2.0.7": + version "2.0.7" + resolved "https://registry.npmmirror.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11" + integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw== + +a-sync-waterfall@^1.0.0: + version "1.0.1" + resolved "https://registry.npmmirror.com/a-sync-waterfall/-/a-sync-waterfall-1.0.1.tgz#75b6b6aa72598b497a125e7a2770f14f4c8a1fa7" + integrity sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA== + +abbrev@^2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/abbrev/-/abbrev-2.0.0.tgz#cf59829b8b4f03f89dda2771cb7f3653828c89bf" + integrity sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ== + +abbrev@^3.0.0: + version "3.0.1" + resolved "https://registry.npmmirror.com/abbrev/-/abbrev-3.0.1.tgz#8ac8b3b5024d31464fe2a5feeea9f4536bf44025" + integrity sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg== + +acorn@^7.1.1: + version "7.4.1" + resolved "https://registry.npmmirror.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== + +agent-base@^7.1.0, agent-base@^7.1.2: + version "7.1.4" + resolved "https://registry.npmmirror.com/agent-base/-/agent-base-7.1.4.tgz#e3cd76d4c548ee895d3c3fd8dc1f6c5b9032e7a8" + integrity sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ== + +ansi-regex@^6.2.2: + version "6.2.2" + resolved "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.2.2.tgz#60216eea464d864597ce2832000738a0589650c1" + integrity sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg== + +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +asap@^2.0.3, asap@~2.0.3: + version "2.0.6" + resolved "https://registry.npmmirror.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== + +assert-never@^1.2.1: + version "1.4.0" + resolved "https://registry.npmmirror.com/assert-never/-/assert-never-1.4.0.tgz#b0d4988628c87f35eb94716cc54422a63927e175" + integrity sha512-5oJg84os6NMQNl27T9LnZkvvqzvAnHu03ShCnoj6bsJwS7L8AO4lf+C/XjK/nvzEqQB744moC6V128RucQd1jA== + +async@^3.2.6: + version "3.2.6" + resolved "https://registry.npmmirror.com/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce" + integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +babel-walk@3.0.0-canary-5: + version "3.0.0-canary-5" + resolved "https://registry.npmmirror.com/babel-walk/-/babel-walk-3.0.0-canary-5.tgz#f66ecd7298357aee44955f235a6ef54219104b11" + integrity sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw== + dependencies: + "@babel/types" "^7.9.6" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +basic-auth@~2.0.1: + version "2.0.1" + resolved "https://registry.npmmirror.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a" + integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg== + dependencies: + safe-buffer "5.1.2" + +binary-extensions@^2.0.0: + version "2.3.0" + resolved "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" + integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== + +bluebird@^3.5.5, bluebird@^3.7.2: + version "3.7.2" + resolved "https://registry.npmmirror.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + +brace-expansion@^1.1.7: + version "1.1.12" + resolved "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843" + integrity sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.1: + version "2.0.2" + resolved "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.2.tgz#54fc53237a613d854c7bd37463aad17df87214e7" + integrity sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.3, braces@~3.0.2: + version "3.0.3" + resolved "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.npmmirror.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + +call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: + version "1.0.2" + resolved "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" + integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + +call-bound@^1.0.2: + version "1.0.4" + resolved "https://registry.npmmirror.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" + integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== + dependencies: + call-bind-apply-helpers "^1.0.2" + get-intrinsic "^1.3.0" + +camel-case@^4.1.2: + version "4.1.2" + resolved "https://registry.npmmirror.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a" + integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw== + dependencies: + pascal-case "^3.1.2" + tslib "^2.0.3" + +character-parser@^2.2.0: + version "2.2.0" + resolved "https://registry.npmmirror.com/character-parser/-/character-parser-2.2.0.tgz#c7ce28f36d4bcd9744e5ffc2c5fcde1c73261fc0" + integrity sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw== + dependencies: + is-regex "^1.0.3" + +chokidar@^3.5.3: + version "3.6.0" + resolved "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" + integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +chokidar@^4.0.3: + version "4.0.3" + resolved "https://registry.npmmirror.com/chokidar/-/chokidar-4.0.3.tgz#7be37a4c03c9aee1ecfe862a4a23b2c70c205d30" + integrity sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA== + dependencies: + readdirp "^4.0.1" + +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +command-exists@^1.2.9: + version "1.2.9" + resolved "https://registry.npmmirror.com/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69" + integrity sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w== + +commander@^5.1.0: + version "5.1.0" + resolved "https://registry.npmmirror.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" + integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== + +compressible@~2.0.18: + version "2.0.18" + resolved "https://registry.npmmirror.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" + integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== + dependencies: + mime-db ">= 1.43.0 < 2" + +compression@^1.7.4: + version "1.8.1" + resolved "https://registry.npmmirror.com/compression/-/compression-1.8.1.tgz#4a45d909ac16509195a9a28bd91094889c180d79" + integrity sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w== + dependencies: + bytes "3.1.2" + compressible "~2.0.18" + debug "2.6.9" + negotiator "~0.6.4" + on-headers "~1.1.0" + safe-buffer "5.2.1" + vary "~1.1.2" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +connect@^3.7.0: + version "3.7.0" + resolved "https://registry.npmmirror.com/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8" + integrity sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ== + dependencies: + debug "2.6.9" + finalhandler "1.1.2" + parseurl "~1.3.3" + utils-merge "1.0.1" + +constantinople@^4.0.1: + version "4.0.1" + resolved "https://registry.npmmirror.com/constantinople/-/constantinople-4.0.1.tgz#0def113fa0e4dc8de83331a5cf79c8b325213151" + integrity sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw== + dependencies: + "@babel/parser" "^7.6.0" + "@babel/types" "^7.6.1" + +cross-spawn@^7.0.3: + version "7.0.6" + resolved "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +cssstyle@^4.1.0: + version "4.6.0" + resolved "https://registry.npmmirror.com/cssstyle/-/cssstyle-4.6.0.tgz#ea18007024e3167f4f105315f3ec2d982bf48ed9" + integrity sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg== + dependencies: + "@asamuzakjp/css-color" "^3.2.0" + rrweb-cssom "^0.8.0" + +data-urls@^5.0.0: + version "5.0.0" + resolved "https://registry.npmmirror.com/data-urls/-/data-urls-5.0.0.tgz#2f76906bce1824429ffecb6920f45a0b30f00dde" + integrity sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg== + dependencies: + whatwg-mimetype "^4.0.0" + whatwg-url "^14.0.0" + +debug@2.6.9: + version "2.6.9" + resolved "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@4, debug@^4.3.2, debug@^4.3.4: + version "4.4.3" + resolved "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== + dependencies: + ms "^2.1.3" + +decimal.js@^10.4.3: + version "10.6.0" + resolved "https://registry.npmmirror.com/decimal.js/-/decimal.js-10.6.0.tgz#e649a43e3ab953a72192ff5983865e509f37ed9a" + integrity sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg== + +deepmerge@^4.2.2: + version "4.3.1" + resolved "https://registry.npmmirror.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== + +define-lazy-prop@^2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" + integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +depd@2.0.0, depd@~2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.npmmirror.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== + +doctypes@^1.1.0: + version "1.1.0" + resolved "https://registry.npmmirror.com/doctypes/-/doctypes-1.1.0.tgz#ea80b106a87538774e8a3a4a5afe293de489e0a9" + integrity sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ== + +dom-serializer@^2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" + integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.2" + entities "^4.2.0" + +domelementtype@^2.3.0: + version "2.3.0" + resolved "https://registry.npmmirror.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" + integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== + +domhandler@^5.0.2, domhandler@^5.0.3: + version "5.0.3" + resolved "https://registry.npmmirror.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31" + integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w== + dependencies: + domelementtype "^2.3.0" + +dompurify@^3.0.3: + version "3.3.3" + resolved "https://registry.npmmirror.com/dompurify/-/dompurify-3.3.3.tgz#680cae8af3e61320ddf3666a3bc843f7b291b2b6" + integrity sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA== + optionalDependencies: + "@types/trusted-types" "^2.0.7" + +domutils@^3.1.0, domutils@^3.2.2: + version "3.2.2" + resolved "https://registry.npmmirror.com/domutils/-/domutils-3.2.2.tgz#edbfe2b668b0c1d97c24baf0f1062b132221bc78" + integrity sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw== + dependencies: + dom-serializer "^2.0.0" + domelementtype "^2.3.0" + domhandler "^5.0.3" + +dunder-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" + integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== + dependencies: + call-bind-apply-helpers "^1.0.1" + es-errors "^1.3.0" + gopd "^1.2.0" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + +ejs@^3.1.6: + version "3.1.10" + resolved "https://registry.npmmirror.com/ejs/-/ejs-3.1.10.tgz#69ab8358b14e896f80cc39e62087b88500c3ac3b" + integrity sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA== + dependencies: + jake "^10.8.5" + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.npmmirror.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== + +encodeurl@~2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" + integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== + +entities@^4.2.0, entities@^4.5.0: + version "4.5.0" + resolved "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== + +entities@^6.0.0: + version "6.0.1" + resolved "https://registry.npmmirror.com/entities/-/entities-6.0.1.tgz#c28c34a43379ca7f61d074130b2f5f7020a30694" + integrity sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g== + +entities@^7.0.1: + version "7.0.1" + resolved "https://registry.npmmirror.com/entities/-/entities-7.0.1.tgz#26e8a88889db63417dcb9a1e79a3f1bc92b5976b" + integrity sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA== + +es-define-property@^1.0.1: + version "1.0.1" + resolved "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: + version "1.1.1" + resolved "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" + integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== + dependencies: + es-errors "^1.3.0" + +es-set-tostringtag@^2.1.0: + version "2.1.0" + resolved "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d" + integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA== + dependencies: + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + has-tostringtag "^1.0.2" + hasown "^2.0.2" + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + +esprima@^4.0.1: + version "4.0.1" + resolved "https://registry.npmmirror.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.npmmirror.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + +fast-archy@^1.0.0: + version "1.0.0" + resolved "https://registry.npmmirror.com/fast-archy/-/fast-archy-1.0.0.tgz#26c8850718de3cecb15200e9257c09f1ad2fa0d0" + integrity sha512-j8CNf0sCX92BogtUZAMPhMj7p3nv7xxeETwfpVWQNQocC5b21FAa6bUzfuhcNoEa4f2RtDe5U3uY0NriH9nA7g== + +fast-equals@^3.0.1: + version "3.0.3" + resolved "https://registry.npmmirror.com/fast-equals/-/fast-equals-3.0.3.tgz#8e6cb4e51ca1018d87dd41982ef92758b3e4197f" + integrity sha512-NCe8qxnZFARSHGztGMZOO/PC1qa5MIFB5Hp66WdzbCRAz8U8US3bx1UTgLS49efBQPcUtO9gf5oVEY8o7y/7Kg== + +fast-text-table@^1.0.1: + version "1.0.1" + resolved "https://registry.npmmirror.com/fast-text-table/-/fast-text-table-1.0.1.tgz#7057f0d58e72eb369601876f9be4ef554ff68254" + integrity sha512-KUwE3MizTzXwhrvTTEpWbug1ngV1zfjwzdxSkeWYGUoVGaaQoid+jxgg4zm4LB+OrtnD+X2xJFq7DCO3pc3fdQ== + +filelist@^1.0.4: + version "1.0.6" + resolved "https://registry.npmmirror.com/filelist/-/filelist-1.0.6.tgz#1e8870942a7c636c862f7c49b9394937b6a995a3" + integrity sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA== + dependencies: + minimatch "^5.0.1" + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@1.1.2: + version "1.1.2" + resolved "https://registry.npmmirror.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.3" + statuses "~1.5.0" + unpipe "~1.0.0" + +form-data@^4.0.0: + version "4.0.5" + resolved "https://registry.npmmirror.com/form-data/-/form-data-4.0.5.tgz#b49e48858045ff4cbf6b03e1805cebcad3679053" + integrity sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + es-set-tostringtag "^2.1.0" + hasown "^2.0.2" + mime-types "^2.1.12" + +fresh@~0.5.2: + version "0.5.2" + resolved "https://registry.npmmirror.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +get-intrinsic@^1.2.6, get-intrinsic@^1.3.0: + version "1.3.0" + resolved "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" + integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== + dependencies: + call-bind-apply-helpers "^1.0.2" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + function-bind "^1.1.2" + get-proto "^1.0.1" + gopd "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + math-intrinsics "^1.1.0" + +get-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" + integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== + dependencies: + dunder-proto "^1.0.1" + es-object-atoms "^1.0.0" + +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob@^7.1.6: + version "7.2.3" + resolved "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +gopd@^1.2.0: + version "1.2.0" + resolved "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" + integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== + +graceful-fs@^4.2.10: + version "4.2.11" + resolved "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +has-symbols@^1.0.3, has-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== + +has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + +hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +hexo-cli@^4.3.2: + version "4.3.2" + resolved "https://registry.npmmirror.com/hexo-cli/-/hexo-cli-4.3.2.tgz#7e7d02da137d9fa461a4356964793c56e92b29e6" + integrity sha512-druJeBgLpG9ncDS5AhBHdAXk0G4CFj8Qes09pApyZ6bR+nJW1JYiDMuilhudaKDdq+1l49jWXVTidkcb7p0Jbw== + dependencies: + abbrev "^2.0.0" + bluebird "^3.7.2" + command-exists "^1.2.9" + hexo-fs "^4.1.1" + hexo-log "^4.0.1" + hexo-util "^3.3.0" + minimist "^1.2.5" + picocolors "^1.0.0" + resolve "^1.20.0" + tildify "^2.0.0" + +hexo-front-matter@^4.2.1: + version "4.2.1" + resolved "https://registry.npmmirror.com/hexo-front-matter/-/hexo-front-matter-4.2.1.tgz#c28ee74f66ab76c98fa73877b5ff7a9cc14161cc" + integrity sha512-sJJI0GNmejYiwBvgnGRKn5V3sbODB4dNPr8jyw2Qp0PRHr4Uuyv8iyxw6WfK3+T7yvzYvJOh+tZ7jnwr2BYARA== + dependencies: + js-yaml "^4.1.0" + +hexo-fs@^4.1.1: + version "4.1.3" + resolved "https://registry.npmmirror.com/hexo-fs/-/hexo-fs-4.1.3.tgz#c64c66e230dad4ec08ca21244327aa0aba69218a" + integrity sha512-Q92zQ5PlVDouvSWFLXQoFSTLIUIODikUJs2BfAXQglyOEjN1dOQn1Z5Nimk/7GHof17R5h/uObCQLnZAjzI2tg== + dependencies: + bluebird "^3.7.2" + chokidar "^3.5.3" + graceful-fs "^4.2.10" + hexo-util "^3.0.1" + +hexo-fs@^5.0.0: + version "5.0.1" + resolved "https://registry.npmmirror.com/hexo-fs/-/hexo-fs-5.0.1.tgz#b5d78d3df3386faadeec364d73e1fed224a4e57a" + integrity sha512-Zm4m21coQA7TtL9JL0oNZF+ypY/HbqwVdaqGqoKD+GEV7HH2Y1YObNBgl1o/lV1tzStC8f15AoRcHdS2jiWp0w== + dependencies: + bluebird "^3.7.2" + chokidar "^4.0.3" + graceful-fs "^4.2.10" + hexo-util "^3.3.0" + +hexo-generator-archive@^2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/hexo-generator-archive/-/hexo-generator-archive-2.0.0.tgz#bd93f17848843bb5bface81103d81f5be1a8b2c9" + integrity sha512-KikJk7dGFbtNHOgqtLFGf5T/S8n1paGp+Gy0KfVDz+HKYhGbXOouyiZkmc3O9KrYt6ja14rmkMhq7KKGtvfehw== + dependencies: + hexo-pagination "3.0.0" + +hexo-generator-category@^2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/hexo-generator-category/-/hexo-generator-category-2.0.0.tgz#55473d5fafaec1cab7a5d9c0da7b159c0133d65c" + integrity sha512-9OduRBf3WeRDa4BR0kAfRjOVHur7v3fm0NKAwbjUiqULigAdNZVZPO3cHKW2MlBbl/lI5PuWdhQ9zZ99CCCAgQ== + dependencies: + hexo-pagination "3.0.0" + +hexo-generator-index@^4.0.0: + version "4.0.0" + resolved "https://registry.npmmirror.com/hexo-generator-index/-/hexo-generator-index-4.0.0.tgz#2f20cf9d231db30edbccd5be32c92c8aeb5d6d63" + integrity sha512-KeM7mOCKWINGFAk1E+CkjMMgqFIv8oaRbGxR7ipkQAp44o4aopkVftma4sdIplOq9WQEWfVYDUK5gEv9J3nzUg== + dependencies: + hexo-pagination "3.0.0" + +hexo-generator-tag@^2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/hexo-generator-tag/-/hexo-generator-tag-2.0.0.tgz#f5a18c92b2f28063a76008b4090a7cea81fbbcc0" + integrity sha512-1px/hF3veEohWDN8jjzchQhaiz+uOStUvvMaBJC9vWOlALh30UFcapL8IrvAwwJZjFRVA+WqGgDRqoQ8+yaaFw== + dependencies: + hexo-pagination "3.0.0" + +hexo-i18n@^2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/hexo-i18n/-/hexo-i18n-2.0.0.tgz#ec7abc160ffef202524f3ff8076b85d5701c447b" + integrity sha512-dkUXecEtChaQMdTHN4WR13c8GwKqjbSOZPJS9qDqV6Ebnb77Wa/nQzWFckhP0dCps3a9lUQBd8hYGOMbOosiQQ== + dependencies: + sprintf-js "^1.1.2" + +hexo-log@^4.0.1, hexo-log@^4.1.0: + version "4.1.0" + resolved "https://registry.npmmirror.com/hexo-log/-/hexo-log-4.1.0.tgz#54b42c250335067b5c60b4137f501607454efda0" + integrity sha512-i2Sgxk8Cgx5viSjq5qW5N/rBFfwoCKQcH8qnnW1fawCapcdEAhIsq+Y3vbrs9bssyDlyU6Vqm4oQmosREaNI7Q== + dependencies: + picocolors "^1.0.0" + +hexo-pagination@3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/hexo-pagination/-/hexo-pagination-3.0.0.tgz#b98b050bbddff25ae646e3d00ad607ac6ae77ea7" + integrity sha512-8oo1iozloZo7TojPVYg4IxL3SJKCBdSJ908fTlIxIK7TWJIKdYnQlW31+12DBJ0NhVZA/lZisPObGF08wT8fKw== + +hexo-renderer-ejs@^2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/hexo-renderer-ejs/-/hexo-renderer-ejs-2.0.0.tgz#56e0c3de5f6b0e1e68b923c65a7c8bfe9ff715eb" + integrity sha512-qCjE1IdwgDgv65qyb0KMVCwCdSVAkH0vwAe9XihjvaKWkmb9dtt8DgErOdqCXn0HReSyWiEVP2BrLRj3gyHwOQ== + dependencies: + ejs "^3.1.6" + +hexo-renderer-marked@^7.0.0: + version "7.0.1" + resolved "https://registry.npmmirror.com/hexo-renderer-marked/-/hexo-renderer-marked-7.0.1.tgz#c3a296187e45d3943011a38ec74b3039118d659a" + integrity sha512-H9IMnDuhjAL24NjWJuqQG0Utdw09159AI531Dbs7jSqmXSXVImoA7SeWIt4XLDRKr1nfWwn+eDziWo4UT6f1fA== + dependencies: + dompurify "^3.0.3" + hexo-util "^3.1.0" + jsdom "^25.0.1" + marked "^15.0.4" + +hexo-renderer-pug@^3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/hexo-renderer-pug/-/hexo-renderer-pug-3.0.0.tgz#4ca631cd0ecccefc43b70ac44230d5008956adc2" + integrity sha512-PmbLx6VkNv+mPLOe97OC4F8iTzTuj665dSYN7bZKArd4M/q7gb2tNs29VGuAOC50i9tvWY2f+tPQimf0GZ9Hyw== + dependencies: + pug "^3.0.2" + +hexo-renderer-stylus@^3.0.1: + version "3.0.1" + resolved "https://registry.npmmirror.com/hexo-renderer-stylus/-/hexo-renderer-stylus-3.0.1.tgz#7ff6f3101fa86ba25de415257be7fa7032e910bc" + integrity sha512-cFm8ZwShBBeFcQwOXc8EK7lIZnSYVD6OJykdL4GBw99hxc4eD5Hlsi32nRzE8sgKv00jhX1s9Da3GVVFMPAVQg== + dependencies: + nib "^1.2.0" + stylus "^0.62.0" + +hexo-server@^3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/hexo-server/-/hexo-server-3.0.0.tgz#fcc597b29b72ee1f035824c5ebd3d92f7e1adb1c" + integrity sha512-u4s0ty9Aew6jV+a9oMrXBwhrRpUQ0U8PWM/88a5aHgDru58VY81mVrxOFxs788NAsWQ8OvsJtF5m7mnXoRnSIA== + dependencies: + bluebird "^3.5.5" + compression "^1.7.4" + connect "^3.7.0" + mime "^3.0.0" + morgan "^1.9.1" + open "^8.0.9" + picocolors "^1.0.0" + serve-static "^1.14.1" + +hexo-theme-butterfly@^5.5.4: + version "5.5.4" + resolved "https://registry.npmmirror.com/hexo-theme-butterfly/-/hexo-theme-butterfly-5.5.4.tgz#3377aa617c723e3bb9260eb1a9ed61fb17700b46" + integrity sha512-gk953p0P1c8QQHBm6xZAoPsYZuIMWsZR+riwT7By+1ygnEv9ZlEWqFflbZbKiXEJWy8VZhkV63k7SiwyifCkSw== + dependencies: + hexo-renderer-pug "^3.0.0" + hexo-renderer-stylus "^3.0.1" + hexo-util "^4.0.0" + moment-timezone "^0.6.0" + +hexo-theme-landscape@^1.0.0: + version "1.1.0" + resolved "https://registry.npmmirror.com/hexo-theme-landscape/-/hexo-theme-landscape-1.1.0.tgz#756335efcc8abd240170f16f36c4af5c4c51c739" + integrity sha512-nszjixBEiEDZjf7glAGbHg0nKGHZdUDwzFVVdRDeu4FeHfGcY5CZNl9sF4vSligjnD8IwFXpDfx5E+GRmNKvWg== + +hexo-util@^3.0.1, hexo-util@^3.1.0, hexo-util@^3.3.0: + version "3.3.0" + resolved "https://registry.npmmirror.com/hexo-util/-/hexo-util-3.3.0.tgz#448927fb22e167f2159306666cc2c7e82c777513" + integrity sha512-YvGngXijE2muEh5L/VI4Fmjqb+/yAkmY+VuyhWVoRwQu1X7bmWodsfYRXX7CUYhi5LqsvH8FAe/yBW1+f6ZX4Q== + dependencies: + camel-case "^4.1.2" + cross-spawn "^7.0.3" + deepmerge "^4.2.2" + highlight.js "^11.6.0" + htmlparser2 "^9.0.0" + prismjs "^1.29.0" + strip-indent "^3.0.0" + +hexo-util@^4.0.0: + version "4.0.0" + resolved "https://registry.npmmirror.com/hexo-util/-/hexo-util-4.0.0.tgz#23f69597aa200ee5612a4fd986cf7d7b5bcc6ea2" + integrity sha512-oXKXBs1HZ2Wu/eq0paAVqtCmAEcqJPZ4xxSVRuwAplm1hFU41Ul53WA5bmYMEz9Dp+OJ/SdchjXRmYlbGJdt3w== + dependencies: + camel-case "^4.1.2" + cross-spawn "^7.0.3" + deepmerge "^4.2.2" + highlight.js "^11.6.0" + htmlparser2 "^10.0.0" + prismjs "^1.29.0" + strip-indent "^3.0.0" + +hexo@^8.0.0: + version "8.1.1" + resolved "https://registry.npmmirror.com/hexo/-/hexo-8.1.1.tgz#fecd2e64b3eaaf281e49bcf82664871277d4114e" + integrity sha512-UnzT4ImjKzMMuVRsvudxrl7MkdZwKe4S9xJN5pQPK9c+K0G+fLFb9kB6CqZZwj2E5ne+QK0ls4XMKqTUbNR3RQ== + dependencies: + abbrev "^3.0.0" + bluebird "^3.7.2" + fast-archy "^1.0.0" + fast-text-table "^1.0.1" + hexo-cli "^4.3.2" + hexo-front-matter "^4.2.1" + hexo-fs "^5.0.0" + hexo-i18n "^2.0.0" + hexo-log "^4.1.0" + hexo-util "^4.0.0" + js-yaml "^4.1.0" + js-yaml-js-types "^1.0.1" + micromatch "^4.0.8" + moize "^6.1.6" + moment "^2.30.1" + moment-timezone "^0.5.46" + nunjucks "^3.2.4" + picocolors "^1.1.1" + pretty-hrtime "^1.0.3" + strip-ansi "^7.1.0" + tildify "^2.0.0" + titlecase "^1.1.3" + warehouse "^6.0.0" + +highlight.js@^11.6.0: + version "11.11.1" + resolved "https://registry.npmmirror.com/highlight.js/-/highlight.js-11.11.1.tgz#fca06fa0e5aeecf6c4d437239135fabc15213585" + integrity sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w== + +html-encoding-sniffer@^4.0.0: + version "4.0.0" + resolved "https://registry.npmmirror.com/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz#696df529a7cfd82446369dc5193e590a3735b448" + integrity sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ== + dependencies: + whatwg-encoding "^3.1.1" + +htmlparser2@^10.0.0: + version "10.1.0" + resolved "https://registry.npmmirror.com/htmlparser2/-/htmlparser2-10.1.0.tgz#fe3f2e12c73b6e462d4e10395db9c1119e4d6ae4" + integrity sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.3" + domutils "^3.2.2" + entities "^7.0.1" + +htmlparser2@^9.0.0: + version "9.1.0" + resolved "https://registry.npmmirror.com/htmlparser2/-/htmlparser2-9.1.0.tgz#cdb498d8a75a51f739b61d3f718136c369bc8c23" + integrity sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.3" + domutils "^3.1.0" + entities "^4.5.0" + +http-errors@~2.0.1: + version "2.0.1" + resolved "https://registry.npmmirror.com/http-errors/-/http-errors-2.0.1.tgz#36d2f65bc909c8790018dd36fb4d93da6caae06b" + integrity sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ== + dependencies: + depd "~2.0.0" + inherits "~2.0.4" + setprototypeof "~1.2.0" + statuses "~2.0.2" + toidentifier "~1.0.1" + +http-proxy-agent@^7.0.2: + version "7.0.2" + resolved "https://registry.npmmirror.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e" + integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig== + dependencies: + agent-base "^7.1.0" + debug "^4.3.4" + +https-proxy-agent@^7.0.5: + version "7.0.6" + resolved "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz#da8dfeac7da130b05c2ba4b59c9b6cd66611a6b9" + integrity sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw== + dependencies: + agent-base "^7.1.2" + debug "4" + +iconv-lite@0.6.3: + version "0.6.3" + resolved "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@^2.0.3, inherits@~2.0.4: + version "2.0.4" + resolved "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-core-module@^2.16.1: + version "2.16.1" + resolved "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" + integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== + dependencies: + hasown "^2.0.2" + +is-docker@^2.0.0, is-docker@^2.1.1: + version "2.2.1" + resolved "https://registry.npmmirror.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + +is-expression@^4.0.0: + version "4.0.0" + resolved "https://registry.npmmirror.com/is-expression/-/is-expression-4.0.0.tgz#c33155962abf21d0afd2552514d67d2ec16fd2ab" + integrity sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A== + dependencies: + acorn "^7.1.1" + object-assign "^4.1.1" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-plain-object@^5.0.0: + version "5.0.0" + resolved "https://registry.npmmirror.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" + integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== + +is-potential-custom-element-name@^1.0.1: + version "1.0.1" + resolved "https://registry.npmmirror.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" + integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== + +is-promise@^2.0.0: + version "2.2.2" + resolved "https://registry.npmmirror.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" + integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== + +is-regex@^1.0.3: + version "1.2.1" + resolved "https://registry.npmmirror.com/is-regex/-/is-regex-1.2.1.tgz#76d70a3ed10ef9be48eb577887d74205bf0cad22" + integrity sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g== + dependencies: + call-bound "^1.0.2" + gopd "^1.2.0" + has-tostringtag "^1.0.2" + hasown "^2.0.2" + +is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.npmmirror.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +jake@^10.8.5: + version "10.9.4" + resolved "https://registry.npmmirror.com/jake/-/jake-10.9.4.tgz#d626da108c63d5cfb00ab5c25fadc7e0084af8e6" + integrity sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA== + dependencies: + async "^3.2.6" + filelist "^1.0.4" + picocolors "^1.1.1" + +js-stringify@^1.0.2: + version "1.0.2" + resolved "https://registry.npmmirror.com/js-stringify/-/js-stringify-1.0.2.tgz#1736fddfd9724f28a3682adc6230ae7e4e9679db" + integrity sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g== + +js-yaml-js-types@^1.0.1: + version "1.0.1" + resolved "https://registry.npmmirror.com/js-yaml-js-types/-/js-yaml-js-types-1.0.1.tgz#fa57135b99d67b3612718426af2d9b314e5c6808" + integrity sha512-5tpfyORs8OQ43alNERbWfYRCtWgykvzYgY46fUhrQi2+kS7N0NuuFYLZ/IrfmVm5muLTndeMublgraXiFRjEPw== + dependencies: + esprima "^4.0.1" + +js-yaml@^4.1.0: + version "4.1.1" + resolved "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.1.1.tgz#854c292467705b699476e1a2decc0c8a3458806b" + integrity sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA== + dependencies: + argparse "^2.0.1" + +jsdom@^25.0.1: + version "25.0.1" + resolved "https://registry.npmmirror.com/jsdom/-/jsdom-25.0.1.tgz#536ec685c288fc8a5773a65f82d8b44badcc73ef" + integrity sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw== + dependencies: + cssstyle "^4.1.0" + data-urls "^5.0.0" + decimal.js "^10.4.3" + form-data "^4.0.0" + html-encoding-sniffer "^4.0.0" + http-proxy-agent "^7.0.2" + https-proxy-agent "^7.0.5" + is-potential-custom-element-name "^1.0.1" + nwsapi "^2.2.12" + parse5 "^7.1.2" + rrweb-cssom "^0.7.1" + saxes "^6.0.0" + symbol-tree "^3.2.4" + tough-cookie "^5.0.0" + w3c-xmlserializer "^5.0.0" + webidl-conversions "^7.0.0" + whatwg-encoding "^3.1.1" + whatwg-mimetype "^4.0.0" + whatwg-url "^14.0.0" + ws "^8.18.0" + xml-name-validator "^5.0.0" + +jsonparse@^1.3.1: + version "1.3.1" + resolved "https://registry.npmmirror.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" + integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg== + +jstransformer@1.0.0: + version "1.0.0" + resolved "https://registry.npmmirror.com/jstransformer/-/jstransformer-1.0.0.tgz#ed8bf0921e2f3f1ed4d5c1a44f68709ed24722c3" + integrity sha512-C9YK3Rf8q6VAPDCCU9fnqo3mAfOH6vUGnMcP4AQAYIEpWtfGLpwOTmZ+igtdK5y+VvI2n3CyYSzy4Qh34eq24A== + dependencies: + is-promise "^2.0.0" + promise "^7.0.1" + +lower-case@^2.0.2: + version "2.0.2" + resolved "https://registry.npmmirror.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" + integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== + dependencies: + tslib "^2.0.3" + +lru-cache@^10.4.3: + version "10.4.3" + resolved "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" + integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== + +marked@^15.0.4: + version "15.0.12" + resolved "https://registry.npmmirror.com/marked/-/marked-15.0.12.tgz#30722c7346e12d0a2d0207ab9b0c4f0102d86c4e" + integrity sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA== + +math-intrinsics@^1.1.0: + version "1.1.0" + resolved "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== + +micro-memoize@^4.1.2: + version "4.2.0" + resolved "https://registry.npmmirror.com/micro-memoize/-/micro-memoize-4.2.0.tgz#76266c42910da4bd6e62c400c1b6204fc9fe6b78" + integrity sha512-dRxIsNh0XosO9sd3aASUabKOzG9dloLO41g74XUGThpHBoGm1ttakPT5in14CuW/EDedkniaShFHbymmmKGOQA== + +micromatch@^4.0.8: + version "4.0.8" + resolved "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +"mime-db@>= 1.43.0 < 2": + version "1.54.0" + resolved "https://registry.npmmirror.com/mime-db/-/mime-db-1.54.0.tgz#cddb3ee4f9c64530dff640236661d42cb6a314f5" + integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.npmmirror.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mime@^3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/mime/-/mime-3.0.0.tgz#b374550dca3a0c18443b0c950a6a58f1931cf7a7" + integrity sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A== + +min-indent@^1.0.0: + version "1.0.1" + resolved "https://registry.npmmirror.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" + integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== + +minimatch@^3.1.1: + version "3.1.5" + resolved "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.5.tgz#580c88f8d5445f2bd6aa8f3cadefa0de79fbd69e" + integrity sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^5.0.1: + version "5.1.9" + resolved "https://registry.npmmirror.com/minimatch/-/minimatch-5.1.9.tgz#1293ef15db0098b394540e8f9f744f9fda8dee4b" + integrity sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw== + dependencies: + brace-expansion "^2.0.1" + +minimist@^1.2.5: + version "1.2.8" + resolved "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +moize@^6.1.6: + version "6.1.7" + resolved "https://registry.npmmirror.com/moize/-/moize-6.1.7.tgz#a67cbb2cf916a9d25d0103b143d3310b3bdba688" + integrity sha512-8/9XgYooMo7/q5tf6zGF4+Wp1jx5sbxV87uK6YvTq9rgDtusLWZCZ8c5C0EhYZbNGudul5EMeMsObO4Ug/gszg== + dependencies: + fast-equals "^3.0.1" + micro-memoize "^4.1.2" + +moment-timezone@^0.5.46: + version "0.5.48" + resolved "https://registry.npmmirror.com/moment-timezone/-/moment-timezone-0.5.48.tgz#111727bb274734a518ae154b5ca589283f058967" + integrity sha512-f22b8LV1gbTO2ms2j2z13MuPogNoh5UzxL3nzNAYKGraILnbGc9NEE6dyiiiLv46DGRb8A4kg8UKWLjPthxBHw== + dependencies: + moment "^2.29.4" + +moment-timezone@^0.6.0: + version "0.6.1" + resolved "https://registry.npmmirror.com/moment-timezone/-/moment-timezone-0.6.1.tgz#c324a71a8285583eb679ced4ba3dcfa53fdce500" + integrity sha512-1B9lmAhB9D9/sHaPC1N7wLFEVUoFldxOpOO96lOD1PvJ43vCd0ozDPbu0FEL3++VvawOlDkq8YD373tJmP5JHw== + dependencies: + moment "^2.29.4" + +moment@^2.29.4, moment@^2.30.1: + version "2.30.1" + resolved "https://registry.npmmirror.com/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae" + integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how== + +morgan@^1.9.1: + version "1.10.1" + resolved "https://registry.npmmirror.com/morgan/-/morgan-1.10.1.tgz#4e02e6a4465a48e26af540191593955d17f61570" + integrity sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A== + dependencies: + basic-auth "~2.0.1" + debug "2.6.9" + depd "~2.0.0" + on-finished "~2.3.0" + on-headers "~1.1.0" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +ms@2.1.3, ms@^2.1.3: + version "2.1.3" + resolved "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +nanoid@^3.3.7: + version "3.3.11" + resolved "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" + integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== + +negotiator@~0.6.4: + version "0.6.4" + resolved "https://registry.npmmirror.com/negotiator/-/negotiator-0.6.4.tgz#777948e2452651c570b712dd01c23e262713fff7" + integrity sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w== + +nib@^1.2.0: + version "1.2.0" + resolved "https://registry.npmmirror.com/nib/-/nib-1.2.0.tgz#cf650a975307edaa8683470430f82ba132bf9f7b" + integrity sha512-7HgrnMl/3yOmWykueO8/D0q+0iWwe7Z+CK2Eaq/xQV8w1hK80WN1oReRQkfkrztbAAnp/nTHkUSl5EcVkor6JQ== + +no-case@^3.0.4: + version "3.0.4" + resolved "https://registry.npmmirror.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" + integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== + dependencies: + lower-case "^2.0.2" + tslib "^2.0.3" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +nunjucks@^3.2.4: + version "3.2.4" + resolved "https://registry.npmmirror.com/nunjucks/-/nunjucks-3.2.4.tgz#f0878eef528ce7b0aa35d67cc6898635fd74649e" + integrity sha512-26XRV6BhkgK0VOxfbU5cQI+ICFUtMLixv1noZn1tGU38kQH5A5nmmbk/O45xdyBhD1esk47nKrY0mvQpZIhRjQ== + dependencies: + a-sync-waterfall "^1.0.0" + asap "^2.0.3" + commander "^5.1.0" + +nwsapi@^2.2.12: + version "2.2.23" + resolved "https://registry.npmmirror.com/nwsapi/-/nwsapi-2.2.23.tgz#59712c3a88e6de2bb0b6ccc1070397267019cf6c" + integrity sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ== + +object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.npmmirror.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww== + dependencies: + ee-first "1.1.1" + +on-finished@~2.4.1: + version "2.4.1" + resolved "https://registry.npmmirror.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + dependencies: + ee-first "1.1.1" + +on-headers@~1.1.0: + version "1.1.0" + resolved "https://registry.npmmirror.com/on-headers/-/on-headers-1.1.0.tgz#59da4f91c45f5f989c6e4bcedc5a3b0aed70ff65" + integrity sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A== + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.npmmirror.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +open@^8.0.9: + version "8.4.2" + resolved "https://registry.npmmirror.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" + integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== + dependencies: + define-lazy-prop "^2.0.0" + is-docker "^2.1.1" + is-wsl "^2.2.0" + +parse5@^7.1.2: + version "7.3.0" + resolved "https://registry.npmmirror.com/parse5/-/parse5-7.3.0.tgz#d7e224fa72399c7a175099f45fc2ad024b05ec05" + integrity sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw== + dependencies: + entities "^6.0.0" + +parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.npmmirror.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +pascal-case@^3.1.2: + version "3.1.2" + resolved "https://registry.npmmirror.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb" + integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +picocolors@^1.0.0, picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: + version "2.3.2" + resolved "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.2.tgz#5a942915e26b372dc0f0e6753149a16e6b1c5601" + integrity sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA== + +pretty-hrtime@^1.0.3: + version "1.0.3" + resolved "https://registry.npmmirror.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" + integrity sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A== + +prismjs@^1.29.0: + version "1.30.0" + resolved "https://registry.npmmirror.com/prismjs/-/prismjs-1.30.0.tgz#d9709969d9d4e16403f6f348c63553b19f0975a9" + integrity sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw== + +promise@^7.0.1: + version "7.3.1" + resolved "https://registry.npmmirror.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" + integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg== + dependencies: + asap "~2.0.3" + +pug-attrs@^3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/pug-attrs/-/pug-attrs-3.0.0.tgz#b10451e0348165e31fad1cc23ebddd9dc7347c41" + integrity sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA== + dependencies: + constantinople "^4.0.1" + js-stringify "^1.0.2" + pug-runtime "^3.0.0" + +pug-code-gen@^3.0.4: + version "3.0.4" + resolved "https://registry.npmmirror.com/pug-code-gen/-/pug-code-gen-3.0.4.tgz#2a82cd317f4983d9b61b51035de34e4f964d788d" + integrity sha512-6okWYIKdasTyXICyEtvobmTZAVX57JkzgzIi4iRJlin8kmhG+Xry2dsus+Mun/nGCn6F2U49haHI5mkELXB14g== + dependencies: + constantinople "^4.0.1" + doctypes "^1.1.0" + js-stringify "^1.0.2" + pug-attrs "^3.0.0" + pug-error "^2.1.0" + pug-runtime "^3.0.1" + void-elements "^3.1.0" + with "^7.0.0" + +pug-error@^2.0.0, pug-error@^2.1.0: + version "2.1.0" + resolved "https://registry.npmmirror.com/pug-error/-/pug-error-2.1.0.tgz#17ea37b587b6443d4b8f148374ec27b54b406e55" + integrity sha512-lv7sU9e5Jk8IeUheHata6/UThZ7RK2jnaaNztxfPYUY+VxZyk/ePVaNZ/vwmH8WqGvDz3LrNYt/+gA55NDg6Pg== + +pug-filters@^4.0.0: + version "4.0.0" + resolved "https://registry.npmmirror.com/pug-filters/-/pug-filters-4.0.0.tgz#d3e49af5ba8472e9b7a66d980e707ce9d2cc9b5e" + integrity sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A== + dependencies: + constantinople "^4.0.1" + jstransformer "1.0.0" + pug-error "^2.0.0" + pug-walk "^2.0.0" + resolve "^1.15.1" + +pug-lexer@^5.0.1: + version "5.0.1" + resolved "https://registry.npmmirror.com/pug-lexer/-/pug-lexer-5.0.1.tgz#ae44628c5bef9b190b665683b288ca9024b8b0d5" + integrity sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w== + dependencies: + character-parser "^2.2.0" + is-expression "^4.0.0" + pug-error "^2.0.0" + +pug-linker@^4.0.0: + version "4.0.0" + resolved "https://registry.npmmirror.com/pug-linker/-/pug-linker-4.0.0.tgz#12cbc0594fc5a3e06b9fc59e6f93c146962a7708" + integrity sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw== + dependencies: + pug-error "^2.0.0" + pug-walk "^2.0.0" + +pug-load@^3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/pug-load/-/pug-load-3.0.0.tgz#9fd9cda52202b08adb11d25681fb9f34bd41b662" + integrity sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ== + dependencies: + object-assign "^4.1.1" + pug-walk "^2.0.0" + +pug-parser@^6.0.0: + version "6.0.0" + resolved "https://registry.npmmirror.com/pug-parser/-/pug-parser-6.0.0.tgz#a8fdc035863a95b2c1dc5ebf4ecf80b4e76a1260" + integrity sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw== + dependencies: + pug-error "^2.0.0" + token-stream "1.0.0" + +pug-runtime@^3.0.0, pug-runtime@^3.0.1: + version "3.0.1" + resolved "https://registry.npmmirror.com/pug-runtime/-/pug-runtime-3.0.1.tgz#f636976204723f35a8c5f6fad6acda2a191b83d7" + integrity sha512-L50zbvrQ35TkpHwv0G6aLSuueDRwc/97XdY8kL3tOT0FmhgG7UypU3VztfV/LATAvmUfYi4wNxSajhSAeNN+Kg== + +pug-strip-comments@^2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/pug-strip-comments/-/pug-strip-comments-2.0.0.tgz#f94b07fd6b495523330f490a7f554b4ff876303e" + integrity sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ== + dependencies: + pug-error "^2.0.0" + +pug-walk@^2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/pug-walk/-/pug-walk-2.0.0.tgz#417aabc29232bb4499b5b5069a2b2d2a24d5f5fe" + integrity sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ== + +pug@^3.0.2: + version "3.0.4" + resolved "https://registry.npmmirror.com/pug/-/pug-3.0.4.tgz#9043ae8dd85fc8beb5becf09dd8e4b754b94b29f" + integrity sha512-kFfq5mMzrS7+wrl5pLJzZEzemx34OQ0w4SARfhy/3yxTlhbstsudDwJzhf1hP02yHzbjoVMSXUj/Sz6RNfMyXg== + dependencies: + pug-code-gen "^3.0.4" + pug-filters "^4.0.0" + pug-lexer "^5.0.1" + pug-linker "^4.0.0" + pug-load "^3.0.0" + pug-parser "^6.0.0" + pug-runtime "^3.0.1" + pug-strip-comments "^2.0.0" + +punycode@^2.3.1: + version "2.3.1" + resolved "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.npmmirror.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +readable-stream@3: + version "3.6.2" + resolved "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdirp@^4.0.1: + version "4.1.2" + resolved "https://registry.npmmirror.com/readdirp/-/readdirp-4.1.2.tgz#eb85801435fbf2a7ee58f19e0921b068fc69948d" + integrity sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg== + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +resolve@^1.15.1, resolve@^1.20.0: + version "1.22.11" + resolved "https://registry.npmmirror.com/resolve/-/resolve-1.22.11.tgz#aad857ce1ffb8bfa9b0b1ac29f1156383f68c262" + integrity sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ== + dependencies: + is-core-module "^2.16.1" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +rfdc@^1.3.0: + version "1.4.1" + resolved "https://registry.npmmirror.com/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca" + integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== + +rrweb-cssom@^0.7.1: + version "0.7.1" + resolved "https://registry.npmmirror.com/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz#c73451a484b86dd7cfb1e0b2898df4b703183e4b" + integrity sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg== + +rrweb-cssom@^0.8.0: + version "0.8.0" + resolved "https://registry.npmmirror.com/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz#3021d1b4352fbf3b614aaeed0bc0d5739abe0bc2" + integrity sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw== + +safe-buffer@5.1.2: + version "5.1.2" + resolved "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@5.2.1, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +"safer-buffer@>= 2.1.2 < 3.0.0": + version "2.1.2" + resolved "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sax@~1.3.0: + version "1.3.0" + resolved "https://registry.npmmirror.com/sax/-/sax-1.3.0.tgz#a5dbe77db3be05c9d1ee7785dbd3ea9de51593d0" + integrity sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA== + +saxes@^6.0.0: + version "6.0.0" + resolved "https://registry.npmmirror.com/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5" + integrity sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA== + dependencies: + xmlchars "^2.2.0" + +send@~0.19.1: + version "0.19.2" + resolved "https://registry.npmmirror.com/send/-/send-0.19.2.tgz#59bc0da1b4ea7ad42736fd642b1c4294e114ff29" + integrity sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg== + dependencies: + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + encodeurl "~2.0.0" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "~0.5.2" + http-errors "~2.0.1" + mime "1.6.0" + ms "2.1.3" + on-finished "~2.4.1" + range-parser "~1.2.1" + statuses "~2.0.2" + +serve-static@^1.14.1: + version "1.16.3" + resolved "https://registry.npmmirror.com/serve-static/-/serve-static-1.16.3.tgz#a97b74d955778583f3862a4f0b841eb4d5d78cf9" + integrity sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA== + dependencies: + encodeurl "~2.0.0" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "~0.19.1" + +setprototypeof@~1.2.0: + version "1.2.0" + resolved "https://registry.npmmirror.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +source-map@^0.7.3: + version "0.7.6" + resolved "https://registry.npmmirror.com/source-map/-/source-map-0.7.6.tgz#a3658ab87e5b6429c8a1f3ba0083d4c61ca3ef02" + integrity sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ== + +sprintf-js@^1.1.2: + version "1.1.3" + resolved "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a" + integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA== + +statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.npmmirror.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== + +statuses@~2.0.2: + version "2.0.2" + resolved "https://registry.npmmirror.com/statuses/-/statuses-2.0.2.tgz#8f75eecef765b5e1cfcdc080da59409ed424e382" + integrity sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw== + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +strip-ansi@^7.1.0: + version "7.2.0" + resolved "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.2.0.tgz#d22a269522836a627af8d04b5c3fd2c7fa3e32e3" + integrity sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w== + dependencies: + ansi-regex "^6.2.2" + +strip-indent@^3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" + integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== + dependencies: + min-indent "^1.0.0" + +stylus@^0.62.0: + version "0.62.0" + resolved "https://registry.npmmirror.com/stylus/-/stylus-0.62.0.tgz#648a020e2bf90ed87587ab9c2f012757e977bb5d" + integrity sha512-v3YCf31atbwJQIMtPNX8hcQ+okD4NQaTuKGUWfII8eaqn+3otrbttGL1zSMZAAtiPsBztQnujVBugg/cXFUpyg== + dependencies: + "@adobe/css-tools" "~4.3.1" + debug "^4.3.2" + glob "^7.1.6" + sax "~1.3.0" + source-map "^0.7.3" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +symbol-tree@^3.2.4: + version "3.2.4" + resolved "https://registry.npmmirror.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" + integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== + +through2@^4.0.2: + version "4.0.2" + resolved "https://registry.npmmirror.com/through2/-/through2-4.0.2.tgz#a7ce3ac2a7a8b0b966c80e7c49f0484c3b239764" + integrity sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw== + dependencies: + readable-stream "3" + +tildify@^2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/tildify/-/tildify-2.0.0.tgz#f205f3674d677ce698b7067a99e949ce03b4754a" + integrity sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw== + +titlecase@^1.1.3: + version "1.1.3" + resolved "https://registry.npmmirror.com/titlecase/-/titlecase-1.1.3.tgz#fc6d65ff582b0602410768ef1a09b70506313dc3" + integrity sha512-pQX4oiemzjBEELPqgK4WE+q0yhAqjp/yzusGtlSJsOuiDys0RQxggepYmo0BuegIDppYS3b3cpdegRwkpyN3hw== + +tldts-core@^6.1.86: + version "6.1.86" + resolved "https://registry.npmmirror.com/tldts-core/-/tldts-core-6.1.86.tgz#a93e6ed9d505cb54c542ce43feb14c73913265d8" + integrity sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA== + +tldts@^6.1.32: + version "6.1.86" + resolved "https://registry.npmmirror.com/tldts/-/tldts-6.1.86.tgz#087e0555b31b9725ee48ca7e77edc56115cd82f7" + integrity sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ== + dependencies: + tldts-core "^6.1.86" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +toidentifier@~1.0.1: + version "1.0.1" + resolved "https://registry.npmmirror.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + +token-stream@1.0.0: + version "1.0.0" + resolved "https://registry.npmmirror.com/token-stream/-/token-stream-1.0.0.tgz#cc200eab2613f4166d27ff9afc7ca56d49df6eb4" + integrity sha512-VSsyNPPW74RpHwR8Fc21uubwHY7wMDeJLys2IX5zJNih+OnAnaifKHo+1LHT7DAdloQ7apeaaWg8l7qnf/TnEg== + +tough-cookie@^5.0.0: + version "5.1.2" + resolved "https://registry.npmmirror.com/tough-cookie/-/tough-cookie-5.1.2.tgz#66d774b4a1d9e12dc75089725af3ac75ec31bed7" + integrity sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A== + dependencies: + tldts "^6.1.32" + +tr46@^5.1.0: + version "5.1.1" + resolved "https://registry.npmmirror.com/tr46/-/tr46-5.1.1.tgz#96ae867cddb8fdb64a49cc3059a8d428bcf238ca" + integrity sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw== + dependencies: + punycode "^2.3.1" + +tslib@^2.0.3: + version "2.8.1" + resolved "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== + +unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.npmmirror.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + +util-deprecate@^1.0.1: + version "1.0.2" + resolved "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.npmmirror.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== + +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.npmmirror.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + +void-elements@^3.1.0: + version "3.1.0" + resolved "https://registry.npmmirror.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09" + integrity sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w== + +w3c-xmlserializer@^5.0.0: + version "5.0.0" + resolved "https://registry.npmmirror.com/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz#f925ba26855158594d907313cedd1476c5967f6c" + integrity sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA== + dependencies: + xml-name-validator "^5.0.0" + +warehouse@^6.0.0: + version "6.0.0" + resolved "https://registry.npmmirror.com/warehouse/-/warehouse-6.0.0.tgz#27f59c24c205d779afe5c42bf969a1b7743ae3b2" + integrity sha512-eOlhyPp5HC951QVDgAeculpSxvfum4UZAbqXSzocqbdiziTseFJFYxmhsKqrQ3wrwgtzPGgkVN2rPL31YLQ0SA== + dependencies: + bluebird "^3.7.2" + graceful-fs "^4.2.10" + hexo-log "^4.0.1" + is-plain-object "^5.0.0" + jsonparse "^1.3.1" + nanoid "^3.3.7" + rfdc "^1.3.0" + through2 "^4.0.2" + +webidl-conversions@^7.0.0: + version "7.0.0" + resolved "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" + integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== + +whatwg-encoding@^3.1.1: + version "3.1.1" + resolved "https://registry.npmmirror.com/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz#d0f4ef769905d426e1688f3e34381a99b60b76e5" + integrity sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ== + dependencies: + iconv-lite "0.6.3" + +whatwg-mimetype@^4.0.0: + version "4.0.0" + resolved "https://registry.npmmirror.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz#bc1bf94a985dc50388d54a9258ac405c3ca2fc0a" + integrity sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg== + +whatwg-url@^14.0.0: + version "14.2.0" + resolved "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-14.2.0.tgz#4ee02d5d725155dae004f6ae95c73e7ef5d95663" + integrity sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw== + dependencies: + tr46 "^5.1.0" + webidl-conversions "^7.0.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.npmmirror.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +with@^7.0.0: + version "7.0.2" + resolved "https://registry.npmmirror.com/with/-/with-7.0.2.tgz#ccee3ad542d25538a7a7a80aad212b9828495bac" + integrity sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w== + dependencies: + "@babel/parser" "^7.9.6" + "@babel/types" "^7.9.6" + assert-never "^1.2.1" + babel-walk "3.0.0-canary-5" + +wrappy@1: + version "1.0.2" + resolved "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +ws@^8.18.0: + version "8.20.0" + resolved "https://registry.npmmirror.com/ws/-/ws-8.20.0.tgz#4cd9532358eba60bc863aad1623dfb045a4d4af8" + integrity sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA== + +xml-name-validator@^5.0.0: + version "5.0.0" + resolved "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-5.0.0.tgz#82be9b957f7afdacf961e5980f1bf227c0bf7673" + integrity sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg== + +xmlchars@^2.2.0: + version "2.2.0" + resolved "https://registry.npmmirror.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" + integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==