在 GitHub 的 Markdown(GFM)中,实现提示、警告等醒目提示框非常简单,它基于块引用(Blockquote)语法进行了扩展。你只需要掌握一个固定的格式即可。 基本语法格式 核心格式是在块引用的第一行使用特殊标记 [!TYPE],后续内容正常以 >开头: > [!TYPE] > 这里放你的提示内容,支持**加粗**、*斜体*、`代码`、列表等大部分 Markdown 语法。 注意:[!TYPE] 中的类型关键字(如 NOTE, WARNING)必须大写。 支持的 5 种类型 GitHub 目前官方支持以下 5 种提示类型,它们在渲染时会带有不同的颜色、图标和标题: [!NOTE](蓝色) 用途:补充说明,用户快速浏览时也应注意的有用信息。 [!TIP](绿色) 用途:建议或小贴士,帮助用户更轻松或更好地完成操作。 [!IMPORTANT](紫) 用途:用户达成目标必须知晓的关键信息。 [!WARNING](黄色/橙色) 用途:急需用户注意的内容,避免潜在问题或风险。 [!CAUTION](红色) 用途:强烈警告,提示某些动作可能导致负面后果(如数据丢失)。 代码示例: > [!NOTE] > 这是一条补充信息。 > [!TIP] > 这是一个小技巧。 > [!IMPORTANT] > 这是关键信息。 > [!WARNING] > 警告,存在潜在风险。 > [!CAUTION] > 危险操作,请极其小心。 解析效果(取决于解释器): Note 这是一条补充信息。 Tip 这是一个小技巧。 Important 这是关键信息。 Warning 警告,存在潜在风险。 Caution 危险操作,请极其小心。 使用注意事项 适用范围:支持在 README.md、.md文件、Issues、Pull Requests、Discussions、Gists 中渲染。 内容支持:提示框内部支持完整的 Markdown 语法,包括段落、列表、代码块、图片等。 使用建议:官方建议仅在对用户成功至关重要时才使用,且尽量限制每篇文章 1 - 2 个,避免连续堆叠使用,防止读者视觉疲劳。 兼容性:这是 GitHub 的扩展语法。在不支持该特性的其他 Markdown 渲染器(如某些旧版编辑器)中,它会降级显示为普通的块引用,不会破坏文档结构...
分类 软件开发 下的文章
告别 UUID?试试这个更优雅的分布式 ID 方案:ULID
在分布式系统和数据库设计中,“如何生成一个唯一的ID” 一直是个经典话题。我们最熟悉的可能是 UUID,但它也有不少槽点:无法排序、存储占用大、可读性差。今天给大家介绍一个非常有潜力的替代品 —— ULID(Universally Unique Lexicographically Sortable Identifier)。 什么是 ULID? ULID 的全称是 通用唯一词典分类标识符。简单来说,它是一种既能保证全局唯一,又天然支持按时间排序 的标识符。 它的设计目标很明确: 全局唯一:不怕分布式环境下的冲突 时间有序:生成顺序 = 时间顺序 紧凑可读:比 UUID 更短、更友好 ULID 长什么样? 一个标准的 ULID 看起来是这样的: 01H4Z7X8J7F9VYMQK7Z9G9Z9Z9 它是基于 Crockford's Base32 编码的(包含 0–9和 A–Z,去除了 I、L、O、U 以避免混淆),生成的字符串通常统一显示为大写,解码时大小写不敏感。 ULID 的结构 ULID 一共 128 位(16 字节),分为两部分: 部分 长度 作用 时间戳 48 位 Unix 毫秒时间(1970–2088) 随机数 80 位 保证唯一性 ┌─────────────── Timestamp (48 bits) ───────────────┐ ┌───────────────────────────────────────────────────┐ │ Time │ Randomness │ └───────────────────────────────────────────────────┘ 为什么要选 ULID? 1、全局唯一性 ULID 结合了时间 + 随机数,即使在高并发的分布式环境中,碰撞概率也极低。 2、天生支持排序 因为时间戳在最前面,ULID 天然按时间递增。你可以直观地看到,随着时间推移,前面的字符会变大: 01H4Z...(较早) 01H5A...(较晚) 这意味着: 数据库按主键排序 ≈ 按时间排序 不需要额外的时间字段 查询更快,索引更高效 Tip 不要试图在 Windows 11 的资源管理器里按文件名排序来验证 ULID 文件名的时间排序,因为该资源管理器目前只支持自然排序。可以尝试换其他资源管理器或在命令行中列出文件。 3、比 UUID 更紧凑 类型 长度 示例 UUID 36 字符 550e8400-e29b-41d4-a716-446655440000 ULID 26 字符 01H4Z7X8J7F9VYMQK7Z9G9Z9Z9 更短 不含 - 更适合 URL / 文件名 4、高性能 & 去中心化 ULID 的生成: 不需要中心服务器 不需要锁 本地即可生成,非常适合微服务、消息队列、日志系统 5、跨平台支持 主流语言几乎都有成熟实现:Java / Go / Python / Rust / JavaScript。 什么时候不该用 ULID? 虽然 ULID 很香,但并不是万能药。以下场景请慎重考虑: ❌ 需要完全随机且无规律的 ID 如果你的业务要求 ID 不能泄露任何时间信息(例如用于订单号防止被爬虫推测销量),ULID 的前缀时间戳可能会暴露生成频率和时间。这种情况下,UUIDv4 这种完全随机的方案更安全。 ❌ 需要绝对连续且无空洞的 ID ULID 是“趋势递增”,不是“绝对连续”。由于随机部分的存在,它会有空洞。如果你的系统要求 ID 必须严格连续(1, 2, 3...),ULID 不适合。 ❌ 存储空间极度敏感 虽然比 UUID 短,但如果是海量数据且不需要排序,单纯的整型自增 ID(Int/Long)仍然是最省空间的。 ULID 适合用在哪些场景? 日志系统 按时间生成、天然有序,非常适合做日志追踪 ID。 分布式系统 不依赖数据库自增 ID,避免单点瓶颈。 数据库主键 B+Tree 索引友好,写入性能更好。 消息队列 消息 ID 自带时间顺序,消费端更容易处理。 总结 ULID 是一个兼顾“唯一性、时间排序、紧凑性”的现代分布式 ID 方案。如果你正在寻找一个比 UUID 更优雅、比雪花算法更简单的方案,不妨试试 ULI...
GitLab 备份与恢复
备份 执行备份命令: sudo gitlab-backup create #如果是使用Docker部署的: sudo docker exec -t <container name> gitlab-backup create 备份完成后会生成备份文件:<backup-id>_gitlab_backup.tar,其中的 <backup-id> 包含了备份时间、GitLab 版本等信息,例如: 1493107454_2018_04_25_10.6.4-ce_gitlab_backup.tar 默认情况下备份的存储位置是:/var/opt/gitlab/backups 此外还应单独备份以下文件: /etc/gitlab/gitlab.rb /etc/gitlab/gitlab-secrets.json /etc/gitlab/ssl /etc/gitlab/trusted-certs 自动删除旧备份 如果想在备份时自动删除旧备份文件,可以修改备份文件的生存期,编辑 /etc/gitlab/gitlab.rb: ## Limit backup lifetime to 7 days - 604800 seconds gitlab_rails['backup_keep_time'] = 604800 修改配置文件后需执行重新配置命令才能生效: sudo gitlab-ctl reconfigure 恢复 从备份文件恢复 GitLab 需要一个可运行的实例,可以重新安装一个全新的程序,但要选用与备份文件一致的版本。恢复时原有数据会被清除! 首先应该手动恢复: /etc/gitlab/gitlab.rb /etc/gitlab/gitlab-secrets.json /etc/gitlab/ssl /etc/gitlab/trusted-certs 重新配置,执行: sudo gitlab-ctl reconfigure 将要恢复的备份文件拷贝至: /var/opt/gitlab/backups/(如果没改备份存储路径的话) 停止连接到数据库的进程: sudo gitlab-ctl stop puma sudo gitlab-ctl stop sidekiq sudo gitlab-ctl status #再次确认他们已被关闭 执行恢复命令: sudo gitlab-backup restore BACKUP=<backup-id> 例如: sudo gitlab-backup restore BACKUP=11493107454_2018_04_25_10.6.4-ce 恢复完成后重新启动并检查 GitLab: sudo gitlab-ctl restart sudo gitlab-rake gitlab:check SANITIZE=true 验证数据库的值是否可以被解密,尤其是在还原了/etc/gitlab/gitlab-secrets.json或更换了服务器: sudo gitlab-rake gitlab:doctor:secrets 为了确保恢复的可靠,还可以对上传文件做完整性检验: sudo gitlab-rake gitlab:artifacts:check sudo gitlab-rake gitlab:lfs:check sudo gitlab-rake gitlab:uploads:chec...
忘记 GitLab 管理员密码后如何修改密码
1、如果 GitLab 是用 Docker 安装的,先进入容器: docker exec -it gitlab bash 2、进入 Rails 控制台: gitlab-rails console 输出: -------------------------------------------------------------------------------- Ruby: ruby 2.7.5p203 (2021-11-24 revision f69aeb8314) [x86_64-linux] GitLab: 15.2.2-ee (4420a6308aa) EE GitLab Shell: 14.9.0 PostgreSQL: 13.6 -----------------------------------------------------------[ booted in 10s ] Loading production environment (Rails 6.1.4.7) irb(main):001:0> 3、查找到管理员对象,以 root 为例: user = User.find_by(username: 'root') pp user.attributes #打印对象的信息,确认一下 4、修改密码: user.password = 'new password' user.save quit 接下来就可以用新密码登录...
修改 pip 源为国内镜像源
由于网络原因,直接使用 pip 默认源会很慢得可怕,这里提供一些国内的镜像,你可以选择使用: 阿里云:https://mirrors.aliyun.com/pypi/simple/ 中国科技大学:https://pypi.mirrors.ustc.edu.cn/simple/ 豆瓣(douban):http://pypi.douban.com/simple/ 清华大学:https://pypi.tuna.tsinghua.edu.cn/simple/ 中国科学技术大学:https://pypi.mirrors.ustc.edu.cn/simple/ 你可以在使用 pip 时通过 -i 参数指定使用上述源: pip install -i https://pypi.tuna.tsinghua.edu.cn/simple some-package 如果想要永久修改,可以在 pip 配置文件中设置。配置文件位置和名称取决于操作系统: Linux/Unix: ~/.pip/pip.conf Windows: %APPDATA%\pip\pip.ini(%APPDATA% 指 C:\Users\用户名\AppData\Roaming) 若文件不存在,则需手动创建。 配置文件内容如下: [global] index-url = https://pypi.tuna.tsinghua.edu.cn/simple 对于使用 http 的源,可以选择性地加上 trusted-host 以信任它,避免出现警告信息。例如: [global] index-url = http://pypi.douban.com/simple trusted-host = pypi.douban.com 这样配置后,你每次使用 pip 安装包时都会默认使用这个...
FTP 协议讲解
FTP 概述 文件传输协议(FTP)作为网络共享文件的传输协议,在网络应用软件中具有广泛的应用。FTP的目标是提高文件的共享性和可靠高效地传送数据。 在传输文件时,FTP 客户端程序先与服务器建立连接,然后向服务器发送命令。服务器收到命令后给予响应,并执行命令。FTP 协议与操作系统无关,任何操作系统上的程序只要符合 FTP 协议,就可以相互传输数据。本文主要基于 LINUX 平台,对 FTP 客户端的实现原理进行详尽的解释并阐述如何使用 C 语言编写一个简单的 FTP 客户端。 FTP 协议 相比其他协议,如 HTTP 协议,FTP 协议要复杂一些。与一般的 C/S 应用不同点在于一般的C/S 应用程序一般只会建立一个 Socket 连接,这个连接同时处理服务器端和客户端的连接命令和数据传输。而FTP协议中将命令与数据分开传送的方法提高了效率。 FTP 使用 2 个端口,一个数据端口和一个命令端口(也叫做控制端口)。这两个端口一般是21 (命令端口)和 20 (数据端口)。控制 Socket 用来传送命令,数据 Socket 是用于传送数据。每一个 FTP 命令发送之后,FTP 服务器都会返回一个字符串,其中包括一个响应代码和一些说明信息。其中的返回码主要是用于判断命令是否被成功执行了。 命令端口 一般来说,客户端有一个 Socket 用来连接 FTP 服务器的相关端口,它负责 FTP 命令的发送和接收返回的响应信息。一些操作如“登录”、“改变目录”、“删除文件”,依靠这个连接发送命令就可完成。 数据端口 对于有数据传输的操作,主要是显示目录列表,上传、下载文件,我们需要依靠另一个 Socket来完成。 如果使用被动模式,通常服务器端会返回一个端口号。客户端需要用另开一个 Socket 来连接这个端口,然后我们可根据操作来发送命令,数据会通过新开的一个端口传输。 如果使用主动模式,通常客户端会发送一个端口号给服务器端,并在这个端口监听。服务器需要连接到客户端开启的这个数据端口,并进行数据的传输。 下面对 FTP 的主动模式和被动模式做一个简单的介绍。 主动模式 (PORT) 主动模式下,客户端随机打开一个大于 1024 的端口向服务器的命令端口 P,即 21 端口,发起连接,同时开放N +1 端口监听,并向服务器发出 “port N+1” 命令,由服务器从它自己的数据端口 (20) 主动连接到客户端指定的数据端口 (N+1)。 FTP 的客户端只是告诉服务器自己的端口号,让服务器来连接客户端指定的端口。对于客户端的防火墙来说,这是从外部到内部的连接,可能会被阻塞。 被动模式 (PASV) 为了解决服务器发起到客户的连接问题,有了另一种 FTP 连接方式,即被动方式。命令连接和数据连接都由客户端发起,这样就解决了从服务器到客户端的数据端口的连接被防火墙过滤的问题。 被动模式下,当开启一个 FTP 连接时,客户端打开两个任意的本地端口 (N > 1024 和 N+1) 。 第一个端口连接服务器的 21 端口,提交 PASV 命令。然后,服务器会开启一个任意的端口 (P > 1024 ),返回如“227 entering passive mode (127,0,0,1,4,18)”。 它返回了 227 开头的信息,在括号中有以逗号隔开的六个数字,前四个指服务器的地址,最后两个,将倒数第二个乘 256 再加上最后一个数字,这就是 FTP 服务器开放的用来进行数据传输的端口。如得到 227 entering passive mode (h1,h2,h3,h4,p1,p2),那么端口号是 p1*256+p2,ip 地址为h1.h2.h3.h4。这意味着在服务器上有一个端口被开放。客户端收到命令取得端口号之后, 会通过 N+1 号端口连接服务器的端口 P,然后在两个端口之间进行数据传输。 主要用到的 FTP 命令 FTP 每个命令都有 3 到 4 个字母组成,命令后面跟参数,用空格分开。每个命令都以 "\r\n"结束。 要下载或上传一个文件,首先要登入 FTP 服务器,然后发送命令,最后退出。这个过程中,主要用到的命令有 USER、PASS、SIZE、REST、CWD、RETR、PASV、PORT、QUIT。 USER: 指定用户名。通常是控制连接后第一个发出的命令。“USER gaoleyi\r\n”: 用户名为gaoleyi 登录。 PASS: 指定用户密码。该命令紧跟 USER 命令后。“PASS gaoleyi\r\n”:密码为 gaoleyi。 SIZE: 从服务器上返回指定文件的大小。“SIZE file.txt\r\n”:如果 file.txt 文件存在,则返回该文件的大小。 CWD: 改变工作目录。如:“CWD dirname\r\n”。 PASV: 让服务器在数据端口监听,进入被动模式。如:“PASV\r\n”。 PORT: 告诉 FTP 服务器客户端监听的端口号,让 FTP 服务器采用主动模式连接客户端。如:“PORT h1,h2,h3,h4,p1,p2”。 RETR: 下载文件。“RETR file.txt \r\n”:下载文件 file.txt。 STOR: 上传文件。“STOR file.txt\r\n”:上传文件 file.txt。 REST: 该命令并不传送文件,而是略过指定点后的数据。此命令后应该跟其它要求文件传输的 FTP 命令。“REST 100\r\n”:重新指定文件传送的偏移量为 100 字节。 QUIT: 关闭与服务器的连接。 FTP 响应码 客户端发送 FTP 命令后,服务器返回响应码。 响应码用三位数字编码表示: 第一个数字给出了命令状态的一般性指示,比如响应成功、失败或不完整。 第二个数字是响应类型的分类,如 2 代表跟连接有关的响应,3 代表用户认证。 第三个数字提供了更加详细的信息。 第一个数字的含义如下: 1 表示服务器正确接收信息,还未处理。 2 表示服务器已经正确处理信息。 3 表示服务器正确接收信息,正在处理。 4 表示信息暂时错误。 5 表示信息永久错误。 第二个数字的含义如下: 0 表示语法。 1 表示系统状态和信息。 2 表示连接状态。 3 表示与用户认证有关的信息。 4 表示未定义。 5 表示与文件系统有关的信息。 Socket 编程的几个重要步骤 Socket 客户端编程主要步骤如下: socket() 创建一个 Socket connect() 与服务器连接 write() 和 read() 进行会话 close() 关闭 Socket Socket 服务器端编程主要步骤如下: socket() 创建一个 Socket bind() listen() 监听 accept() 接收连接的请求 write() 和 read() 进行会话 close() 关闭 Socket 实现 FTP 客户端上传下载功能 下面让我们通过一个例子来对 FTP 客户端有一个深入的了解。本文实现的 FTP 客户端有下列功能: 客户端和 FTP 服务器建立 Socket 连接。 向服务器发送 USER、PASS 命令登录 FTP 服务器。 使用 PASV 命令得到服务器监听的端口号,建立数据连接。 使用 RETR/STOR 命令下载/上传文件。 在下载完毕后断开数据连接并发送 QUIT 命令退出。 本例中使用的 FTP 服务器为 filezilla。在整个交互的过程中,控制连接始终处于连接的状态,数据连接在每传输一个文件时先打开,后关闭。 客户端和 FTP 服务器建立 Socket 连接 当客户端与服务器建立连接后,服务器会返回 220 的响应码和一些欢迎信息。 图 1. 客户端连接到服务器端 清单 1. 客户端连接到 FTP 服务器,接收欢迎信息 SOCKET control_sock; struct hostent *hp; struct sockaddr_in server; memset(&server, 0, sizeof(struct sockaddr_in)); /* 初始化socket */ control_sock = socket(AF_INET, SOCK_STREAM, 0); hp = gethostbyname(server_name); memcpy(&server.sin_addr, hp->h_addr, hp->h_length); server.sin_family = AF_INET; server.sin_port = htons(port); /* 连接到服务器端 */ connect(control_sock,(struct sockaddr *)&server, sizeof(server)); /* 客户端接收服务器端的一些欢迎信息 */ read(control_sock, read_buf, read_len); 客户端登录 FTP 服务器 当客户端发送用户名和密码,服务器验证通过后,会返回 230 的响应码。然后客户端就可以向服务器端发送命令了。 图 2. 客户端登录 FTP 服务器 清单 2. 客户端发送用户名和密码,登入 FTP 服务器 /* 命令 ”USER username\r\n” */ sprintf(send_buf,"USER %s\r\n",username); /*客户端发送用户名到服务器端 */ write(control_sock, send_buf, strlen(send_buf)); /* 客户端接收服务器的响应码和信息,正常为 ”331 User name okay, need password.” */ read(control_sock, read_buf, read_len); /* 命令 ”PASS password\r\n” */ sprintf(send_buf,"PASS %s\r\n",password); /* 客户端发送密码到服务器端 */ write(control_sock, send_buf, strlen(send_buf)); /* 客户端接收服务器的响应码和信息,正常为 ”230 User logged in, proceed.” */ read(control_sock, read_buf, read_len); 客户端让 FTP 服务器进入被动模式 当客户端在下载/上传文件前,要先发送命令让服务器进入被动模式。服务器会打开数据端口并监听。并返回响应码 227 和数据连接的端口号。 图 3. 客户端让服务器进入被动模式 清单 3. 让服务器进入被动模式,在数据端口监听 /* 命令 ”PASV\r\n” */ sprintf(send_buf,"PASV\r\n"); /* 客户端告诉服务器用被动模式 */ write(control_sock, send_buf, strlen(send_buf)); /*客户端接收服务器的响应码和新开的端口号, * 正常为 ”227 Entering passive mode (<h1,h2,h3,h4,p1,p2>)” */ read(control_sock, read_buf, read_len); 客户端通过被动模式下载文件 当客户端发送命令下载文件。服务器会返回响应码 150,并向数据连接发送文件内容。 图 4. 客户端从FTP服务器端下载文件 清单 4. 客户端连接到 FTP 服务器的数据端口并下载文件 /* 连接服务器新开的数据端口 */ connect(data_sock,(struct sockaddr *)&server, sizeof(server)); /* 命令 ”CWD dirname\r\n” */ sprintf(send_buf,"CWD %s\r\n", dirname); /* 客户端发送命令改变工作目录 */ write(control_sock, send_buf, strlen(send_buf)); /* 客户端接收服务器的响应码和信息,正常为 ”250 Command okay.” */ read(control_sock, read_buf, read_len); /* 命令 ”SIZE filename\r\n” */ sprintf(send_buf,"SIZE %s\r\n",filename); /* 客户端发送命令从服务器端得到下载文件的大小 */ write(control_sock, send_buf, strlen(send_buf)); /* 客户端接收服务器的响应码和信息,正常为 ”213 <size>” */ read(control_sock, read_buf, read_len); /* 命令 ”RETR filename\r\n” */ sprintf(send_buf,"RETR %s\r\n",filename); /* 客户端发送命令从服务器端下载文件 */ write(control_sock, send_buf, strlen(send_buf)); /* 客户端接收服务器的响应码和信息,正常为 ”150 Opening data connection.” */ read(control_sock, read_buf, read_len); /* 客户端创建文件 */ file_handle = open(disk_name, CRFLAGS, RWXALL); for( ; ; ) { ... ... /* 客户端通过数据连接 从服务器接收文件内容 */ read(data_sock, read_buf, read_len); /* 客户端写文件 */ write(file_handle, read_buf, read_len); ... ... } /* 客户端关闭文件 */ rc = close(file_handle); 客户端退出服务器 当客户端下载完毕后,发送命令退出服务器,并关闭连接。服务器会返回响应码 200。 图 5. 客户端从 FTP 服务器退出 清单 5. 客户端关闭数据连接,退出 FTP 服务器并关闭控制连接 /* 客户端关闭数据连接 */ close(data_sock); /* 客户端接收服务器的响应码和信息,正常为 ”226 Transfer complete.” */ read(control_sock, read_buf, read_len); /* 命令 ”QUIT\r\n” */ sprintf(send_buf,"QUIT\r\n"); /* 客户端将断开与服务器端的连接 */ write(control_sock, send_buf, strlen(send_buf)); /* 客户端接收服务器的响应码,正常为 ”200 Closes connection.” */ read(control_sock, read_buf, read_len); /* 客户端关闭控制连接 */ close(control_sock); 至此,下载文件已经完成。需要注意的是发送 FTP 命令的时候,在命令后要紧跟 “\r\n”,否则服务器不会返回信息。回车换行符号 “\r\n” 是 FTP 命令的结尾符号,当服务器接收到这个符号时,认为客户端发送的命令已经结束,开始处理。否则会继续等待。 让我们来看一下 FTP 服务器这一端的响应情况: 清单 6. 客户端下载文件时,FTP 服务器的响应输出 (not logged in) (127.0.0.1)> Connected, sending welcome message... (not logged in) (127.0.0.1)> 220-FileZilla Server version 0.9.36 beta (not logged in) (127.0.0.1)> 220 hello gaoleyi (not logged in) (127.0.0.1)> USER gaoleyi (not logged in) (127.0.0.1)> 331 Password required for gaoleyi (not logged in) (127.0.0.1)> PASS ********* gaoleyi (127.0.0.1)> 230 Logged on gaoleyi (127.0.0.1)> PWD gaoleyi (127.0.0.1)> 257 "/" is current directory. gaoleyi (127.0.0.1)> SIZE file.txt gaoleyi (127.0.0.1)> 213 4096 gaoleyi (127.0.0.1)> PASV gaoleyi (127.0.0.1)> 227 Entering Passive Mode (127,0,0,1,13,67) gaoleyi (127.0.0.1)> RETR file.txt gaoleyi (127.0.0.1)> 150 Connection accepted gaoleyi (127.0.0.1)> 226 Transfer OK gaoleyi (127.0.0.1)> QUIT gaoleyi (127.0.0.1)> 221 Goodbye 首先,服务器准备就绪后返回 220。客户端接收到服务器端返回的响应码后,相继发送“USER username” 和 “PASS password” 命令登录。随后,服务器返回的响应码为 230 开头,说明客户端已经登入了。这时,客户端发送 PASV 命令让服务器进入被动模式。服务器返回如 “227 Entering Passive Mode (127,0,0,1,13,67)”,客户端从中得到端口号,然后连接到服务器的数据端口。接下来,客户端发送下载命令,服务器会返回响应码 150,并从数据端口发送数据。最后,服务器返回 “226 transfer complete”,表明数据传输完成。 需要注意的是,客户端不要一次发送多条命令,例如我们要打开一个目录并且显示这个目录,我们得发送 CWD dirname,PASV,LIST。在发送完 CWD dirname 之后等待响应代码,然后再发送后面一条。当 PASV 返回之后,我们打开另一个 Socket 连接到相关端口上。然后发送 LIST,返回 125 之后在开始接收数据,最后返回 226 表明完成。 在传输多个文件的过程中,需要注意的是每次新的传输都必须重新使用 PASV 获取新的端口号,接收完数据后应该关闭该数据连接,这样服务器才会返回一个 2XX 成功的响应。然后客户端可以继续下一个文件的传输。 上传文件与下载文件相比,登入验证和切换被动模式都如出一辙,只需要改变发送到服务器端的命令,并通过数据连接发送文件内容。 客户端通过被动模式向服务器上传文件 当客户端发送命令上传文件,服务器会从数据连接接收文件。 图 6. 客户端连接到 FTP 服务器的数据端口并上传文件 客户端通过主动模式向服务器上传文件 到目前为止,本文介绍的都是客户端用被动模式进行文件的上传和下载。下面将介绍客户端用主动模式下载文件。 图 7. 用主动模式从 FTP 服务器下载文件 清单 7. 用主动模式从 FTP 服务器下载文件的示例 C 程序 ... ... SOCKET data_sock; data_sock = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in name; name.sin_family = AF_INET; name.sin_addr.s_addr = htons(INADDR_ANY); server_port = p1*256+p2; length = sizeof(name); name.sin_port = htons(server_port); bind(server_sock, (struct sockaddr *)&name, length); struct sockaddr_in client_name; length = sizeof(client_name); /* 客户端开始监听端口p1*256+p2 */ listen(server_sock, 64); /* 命令 ”PORT \r\n” */ sprintf(send_buf,"PORT 1287,0,0,1,%d,%d\r\n", p1, p2); write(control_sock, send_buf,strlen(send_buf)); /* 客户端接收服务器的响应码和信息,正常为 ”200 Port command successful” */ read(control_sock, read_buf, read_len); sprintf(send_buf,"RETR filename.txt\r\n"); write(control_sock, send_buf, strlen(send_buf)); /* 客户端接收服务器的响应码和信息,正常为 ”150 Opening data channel for file transfer.” */ read(control_sock, read_buf, read_len); /* ftp客户端接受服务器端的连接请求 */ data_sock = accept(server_sock,(struct sockaddr *)&client_name, &length); ... ... file_handle = open(disk_name, ROFLAGS, RWXALL); for( ; ; ) { ... ... read(data_sock, read_buf, read_len); write(file_handle, read_buf, read_len); ... ... } close(file_handle); 客户端通过 PORT 命令告诉服务器连接自己的 p1*256+p2 端口。随后在这个端口进行监听,等待 FTP 服务器连接上来, 再通过这个数据端口来传输文件。PORT 方式在传送数据时,FTP 客户端其实就相当于一个服务器端,由 FTP 服务器主动连接自己。 断点续传 由于网络不稳定,在传输文件的过程中,可能会发生连接断开的情况,这时候需要客户端支持断点续传的功能,下次能够从上次终止的地方开始接着传送。需要使用命令 REST。如果在断开连接前,一个文件已经传输了 512 个字节。则断点续传开始的位置为 512,服务器会跳过传输文件的前 512 字节。 清单 8. 从 FTP 服务器断点续传下载文件 ... ... /* 命令 ”REST offset\r\n” */ sprintf(send_buf,"REST %ld\r\n", offset); /* 客户端发送命令指定下载文件的偏移量 */ write(control_sock, send_buf, strlen(send_buf)); /* 客户端接收服务器的响应码和信息, *正常为 ”350 Restarting at <position>. Send STORE or RETRIEVE to initiate transfer.” */ read(control_sock, read_buf, read_len); ... ... /* 命令 ”RETR filename\r\n” */ sprintf(send_buf,"RETR %s\r\n",filename); /* 客户端发送命令从服务器端下载文件, 并且跳过该文件的前offset字节*/ write(control_sock, send_buf, strlen(send_buf)); /* 客户端接收服务器的响应码和信息,* *正常为 ”150 Connection accepted, restarting at offset <position>” */ read(control_sock, read_buf, read_len); ... ... file_handle = open(disk_name, CRFLAGS, RWXALL); /* 指向文件写入的初始位置 */ lseek(file_handle, offset, SEEK_SET); ... ... 结束语 本文从应用实现的角度,介绍了 FTP 协议。并用详尽的例子分析了如何用主动模式和被动模式实现 FTP 客户端上传下载文件,如何进行断点续传。通过本文可以让读者对 FTP 客户端的原理有一个深入的了...
Qt 源码编译 configure 参数列表
configure meta -help,-h ............显示此帮助屏幕 -verbose,-v .........在配置期间输出详细消息 -continue............尽管有错误仍然继续配置 -redo ................用以前使用的选项重新配置。其他选项可能会通过,但不会保存以供-redo稍后使用。 -recheck .............放弃缓存的负配置测试结果。安装缺失的依赖关系后使用它。 -recheck-all .........放弃所有缓存的配置测试结果。 -feature- <特征> ...启用<特征> -no-feature- <feature>禁用<feature> [none] -list-features .......列出可用功能。请注意一些功能也有专用的命令行选项。 -list-libraries ......列出可能的外部依赖关系。 Build options -opensource ..........构建Qt的开源版本 -commercial ..........构建Qt的商业版 -confirm-license .....自动确认许可证 -release.............关闭调试版本的Qt [yes] -debug ...............打开调试生成Qt [no] -debug-and-release ...构建两个版本的Qt,包含和不包含打开调试[是](仅适用于Apple和Windows) -optimize-debug ......在调试版本中启用调试友好的优化[自动](MSVC不支持) -optimize-size .......优化发布版本的大小而不是速度[no] -optimized-tools .....甚至在调试版本中构建优化的主机工具[no] -force-debug-info ....为发布版本创建符号文件[no] -separate-debug-info。分离调试信息以分离文件[no] -strip ...............释放不需要的符号的二进制文件[是] -force-asserts .......即使在发布版本中启用Q_ASSERT [no] -developer-build .....编译并链接Qt以开发Qt本身(用于自动测试的出口,额外检查等)[no] -shared..............建立共享的Qt库[是](不适用于UIKit) -static ..............构建静态Qt库[no](对于UIKit是) -framework ...........构建Qt框架包[是](仅限Apple) -platform <target> ...选择主机mkspec [检测到] -xplatform <target> ..交叉编译时选择target mkspec [PLATFORM] -device <name> .......交叉编译设备<name> -device-option <key = value> ...为设备mkspec添加选项 -appstore-compliant ..禁用平台应用商店中不允许使用的代码。默认情况下,默认情况下,默认情况下,平台需要通过默认应用商店进行分发,特别是Android,iOS,tvOS,watchOS和Universal Windows Platform。 [汽车] -qtnamespace <name> ..将所有Qt库代码封装在'namespace <name> {...}'中。 -qtlibinfix <infix>将所有libQt5 * .so重命名为libQt5 * <infix> .so。 -testcocoon ..........带有TestCocoon代码覆盖工具的仪器[no] -gcov ................具有GCov代码覆盖工具的仪器[no] -sanitize {address | thread | memory | undefined}仪器与指定的编译器消毒剂。 -c ++ std <edition> ....选择C ++标准<edition> [c ++ 1z / c ++ 14 / c ++ 11](不支持MSVC) -sse2 ................使用SSE2指令[自动] -sse3 / -ssse3 / -sse4.1 / -sse4.2 / -avx / -avx2 / -avx512启用特定的x86指令[auto]启用的仍然受到运行时检测。 -mips_dsp / -mips_dspr2使用MIPS DSP / rev2指令[auto] -qreal <type> ........ typedef qreal到指定的类型。 [双]注意:这会影响二进制兼容性。 -R <string> ..........为Qt添加一个显式的运行时库路径库。支持相对于LIBDIR的路径。 -rpath ...............使用库链接Qt库和可执行文件将路径安装为运行时库路径。如同-R LIBDIR。在苹果平台上,禁用这意味着使用绝对安装名称(基于 LIBDIR)动态库和框架。 [汽车]减少输出......减少输出符号的数量[自动] -reuce-relocations ..减少重定位量[auto](仅适用于Unix) -plugin-manifests ....将清单嵌入插件[no](仅限Windows) -static-runtime ......使用-static,使用静态运行时[no](仅限Windows) -pch .................使用预编译头文件[auto] -ltcg ................使用链接时间码生成[no] -use-gold-linker .....使用GNU gold链接器[auto] -incredibuild-xge ....使用IncrediBuild XGE [no](仅限Windows) -ccache ..............使用ccache编译器缓存[no](仅适用于Unix) -make-tool <tool> ....使用<tool>构建qmake [nmake](仅适用于Windows) -mp ..................使用多个处理器进行编译(仅限MSVC) -warnings-are-errors。将警告视为错误[no; yes如果-developer-build] -silent ..............减少构建输出以便发出警告和错误可以更容易地看到 Build environment -sysroot <dir> ....... 将<dir>设置为目标sysroot -gcc-sysroot ......... 使用-sysroot,将编译器通过--sysroot [yes] -pkg-config ..........使用pkg-config [auto](仅适用于Unix) -D <string> ..........传递附加的预处理器定义 -I <string> ..........传递额外的包含路径 -L <string> ..........传递额外的库路径 -F <string> ..........传递额外的框架路径(仅适用于Apple) -sdk <sdk> ...........使用Apple提供的SDK <sdk>构建Qt。争论应该是以下列出的可用SDK之一'xcodebuild -showsdks'。请注意,该参数仅适用于Qt库和使用目标mkspec构建的应用程序 - 不是主机工具,如qmake,moc,rcc等。 -android-sdk path ....设置Android SDK根路径[$ ANDROID_SDK_ROOT] -android-ndk路径....设置Android NDK根路径[$ ANDROID_NDK_ROOT] -android-ndk-platform设置Android平台 -android-ndk-host ....设置Android NDK主机(linux-x86,linux-x86_64等)[$ ANDROID_NDK_HOST] -android-arch ........设置Android体系结构(armeabi,armeabi-v7a,arm64-v8a,x86,x86_64,mips,mips64) -android-toolchain-version ...设置Android工具链版本 -android-style-assets自动从设备中提取样式资产运行。此选项使Android样式表现良好正确的,但也使得Android平台插件与LGPL2.1不兼容。 [是] Component selection -skip <repo> .........从构建中排除整个存储库。 -make <part> .........将<part>添加到要构建的零件列表中。指定此选项将首先清除默认列表。[库和例子,如果不是交叉构建也是工具,还测试是否 - 开发人员构建] -nomake <part> .......从要构建的零件列表中排除<part>。 -compile-examples ....未设置时,只安装示例的源代码[是] -gui .................构建Qt GUI模块和依赖[yes] -widgets .............编译Qt Widgets模块和依赖[yes] -no-dbus .............不要构建Qt D-Bus模块[Android和Windows默认] -dbus-linked .........构建Qt D-Bus并链接到libdbus-1 [auto] -dbus-runtime ........构建Qt D-Bus并动态加载libdbus-1 [no] -accessibility.......启用可访问性支持[是]注意:不建议禁用可访问性。 -qml-debug ...........启用QML调试支持[yes] Qt附带一些第三方库的捆绑副本。这些被使用默认情况下,如果自动检测相应的系统库失败。 Core options -doubleconversion ....选择使用的双转换库[system / qt / no]没有暗示使用sscanf_l和snprintf_l(不精确)。 -glib ................启用Glib支持[no;在Unix上自动] -eventfd .............启用eventfd支持 -inotify .............启用inotify支持 -iconv ...............启用iconv(3)支持[posix / sun / gnu / no](仅适用于Unix) -icu .................启用ICU支持[自动] -pcre ................选择使用的libpcre2 [system / qt] -pps .................启用PPS支持[自动](仅限QNX) -zlib ................选择用过的zlib [system / qt] Logging backends -Journald ..........启用日志支持[no](仅限Unix) -syslog ............启用syslog支持[no](仅适用于Unix) -slog2 .............启用slog2支持[自动](仅限QNX) Network options -ssl .................启用SSL支持方法[自动] -no-openssl ..........不要使用OpenSSL [Apple和WinRT上的默认] -openssl-linked ......使用OpenSSL并链接到libssl [no] -openssl-runtime .....使用OpenSSL并动态加载libssl [auto] -securetransport .....使用SecureTransport [auto](仅限Apple) -sctp ................启用SCTP支持[no] -libproxy ............启用libproxy的使用[no] -system-proxies ......默认使用系统网络代理[yes] GUI, printing, widget options -cups ................启用CUPS支持[自动](仅适用于Unix) -fontconfig ..........启用Fontconfig支持[auto](仅适用于Unix) -freetype ............选择使用的FreeType [system / qt / no] -harfbuzz ............选择用过的HarfBuzz-NG [系统/ qt / no](不在Apple和Windows上自动检测) -gtk .................启用GTK平台主题支持[auto] -lgmon ...............启用lgmon支持[自动](仅限QNX) -no-opengl ...........禁用OpenGL支持 -opengl <api> ........启用OpenGL支持。支持的API:es2(在Windows上默认),桌面(在Unix上默认),动态(仅限Windows) -opengles3 ...........启用OpenGL ES 3.x支持而不是ES 2.x [自动] -angle ...............使用捆绑的ANGLE支持OpenGL ES 2.0 [自动](仅限Windows) -combined-angle-lib ..将LibEGL和LibGLESv2合并到LibANGLE(仅限Windows) -qpa <name> ..........选择默认的QPA后端(例如,xcb,cocoa,windows) -xcb-xlib .............启用Xcb-Xlib支持[auto] Platform backends -direct2d ..........启用Direct2D支持[自动](仅限Windows) -directfb ..........启用DirectFB支持[no](仅适用于Unix) -eglfs .............启用EGLFS支持[auto;没有在Android和Windows上] -gbm ...............为GBM [auto]启用后端(仅限Linux) -kms ...............启用KMS [auto]的后端(仅适用于Linux) -linuxfb ...........启用Linux Framebuffer支持[auto](仅限Linux) -mirclient .........启用Mir客户端支持[no](仅Linux) -xcb ...............选择使用的xcb- *库[system / qt / no](-qt-xcb仍然使用libxcb本身的系统版本) Input backends -evdev .............启用evdev支持[auto] -imf ...............启用IMF支持[自动](仅限QNX) -libinput ..........启用libinput支持[auto] -mtdev .............启用mtdev支持[auto] -tslib .............启用tslib支持[自动] -xinput2 ...........启用XInput2支持[自动] -xkbcommon-x11 .....选择与xcb结合使用的xkbcommon[系统/ QT / NO] -xkb-config-root <dir> ...使用-qt-xkbcommon-x11,设置默认的XKB配置根目录<dir> [检测] -xkbcommon-evdev ...启用X-less xkbcommon与libinput结合使用[汽车] Image formats -gif ...............启用对GIF的读取支持[自动] -ico ...............启用对ICO的支持[是] -libpng ............选择用过的libpng [system / qt / no] -libjpeg ...........选择使用的libjpeg [system / qt / no] Database options -sql- <driver> ........启用SQL <驱动程序>插件。支持的驱动db2 ibase mysql oci odbc psql sqlite2 sqlite tds[全自动] -sqlite ..............选择用过的sqlite3 [系统/ qt] Qt3D options -assimp ..............选择使用的assimp库[system / qt / no] -qt3d-profile-jobs ...启用作业分析[no] -qt3d-profile-gl .....启用OpenGL分析[no] -qt3d-simd ...........选择SIMD支持级别[no / sse2 / avx2] -qt3d-render .........启用Qt3D渲染方面[是] -qt3d-input ..........启用Qt3D输入方面[是] -qt3d-logic ..........启用Qt3D逻辑方面[是] -qt3d-extras .........启用Qt3D Extras方面[yes] -qt3d-animation .......启用Qt3D动画方面[是] Multimedia options -pulseaudio ..........启用PulseAudio支持[自动](仅适用于Unix) -alsa ................启用ALSA支持[自动](仅适用于Unix) -no-gstreamer ........禁用对GStreamer的支持 -gstreamer [版本]。启用GStreamer支持[自动]在没有参数的情况下,首先尝试1.0,然后再尝试0.10。 -mediaplayer-backend <名称> ...选择媒体播放器后端(仅限Windows)支持的后端:directshow(默认),wmf Webengine options -webengine-alsa ................启用ALSA支持[自动](仅限Linux) -webengine-pulseaudio ..........启用PulseAudio支持[自动](仅限Linux) -webengine-embedded-build ......启用Linux嵌入式构建[auto](仅限Linux) -webengine-icu .................使用系统ICU库[system / qt](仅限Linux) -webengine -ffmpeg ..............使用系统FFmpeg库[system / qt](仅限Linux) -webengine-opus ................使用系统Opus库[system / qt](仅限Linux) -webengine-webp ................使用系统WebP库[system / qt](仅限Linux) -webengine-pepper-plugins ......启用Pepper Flash和Widevine插件[自动] -webengine-printing-and-pdf ....启用打印和输出到PDF[汽车] -webengine-proprietary-codecs ..启用对专有编解码器的支持[no] -webengine-spellchecker ........启用对拼写检查程序的支持[是] -webengine-native-spellchecker。启用对原生拼写检查器的支持[否](仅限macOS) -webengine-webrtc ..............启用对WebRTC的支持[自动...
使用 OpenSSL 生成和签发 SSL 证书
SSL 证书是数字证书的一种,也称为服务器 SSL 证书。它遵守 SSL 协议,由受信任的数字证书颁发机构 CA 在验证服务器身份后颁发。SSL 证书具有服务器身份验证和数据传输加密功能,可以确保在客户端和服务器之间的信息传输过程中,数据不被改变,保证数据的完整性,并防止信息泄露。 为什么需要证书 设想用户 A 要欺骗用户 B,A 可以向 B 发送一份伪造是 C 发送的报文,A 用自己的私钥进行数字签名,并附上 A 自己的公钥,谎称这公钥是 C 的。B 如何知道这个公钥不是 C 的呢? 那假如说你(客户端)拿到的公钥并不是服务端给的呢,而是黑客塞给你的呢?而你却把这个假公钥当成真的,那么当你使用这个假公钥加密一些敏感信息时,黑客就可以截取你的这段信息,由于这信息是用黑客自己的公钥加密的,这样一来,黑客拿自己的私钥就能解密得到你的敏感信息。这就是问题所在。 要解决这个问题,其实只要保证“公钥”是可信的。只有服务端发给你的公钥你才能拿,而坏人给你的公钥,你要懂得识别并丢弃它。 显然,这需要有一个值得信赖的机构来将公钥与其对应的实体(人或机器)进行绑定 (binding)。这样的机构就叫做认证中心 CA (Certification Authority)。 那么数字证书究竟是什么东西?其实它就是一个文件,包含如下信息: 公钥的提供方信息 公钥 数字证书中的公钥,能够与服务端的私钥配对使用,实现数据传输过程中的加密和解密。 CA机构的签名 证书有效时间 证书采用的加密算法 证书生成过程 在自己的服务器上生成一对公钥和私钥。然后将域名、申请者、公钥(注意不是私钥,私钥是无论如何也不能泄露的)等其他信息整合在一起,生成 .csr 文件。将这个 .csr 文件发给 CA 机构,CA 机构收到申请后,会通过各种手段验证申请者的组织信息和个人信息,如无异常(组织存在,企业合法,确实是域名的拥有者),CA 就会使用散列算法对 .csr 里的明文信息先做一个 HASH,得到一个信息摘要,再用 CA 自己的私钥对这个信息摘要进行加密,生成一串密文,密文即是所说的签名。签名 + .csr 明文信息,即是证书。CA 把这个证书返回给申请人。 SSL 证书根据验证级别可以分为域名型 SSL 证书、企业型 SSL 证书、增强型 SSL 证书。按照域名数量,又可以分为单域名 SSL 证书、多域名版 SSL 证书、通配符 SSL 证书。 按照用途分为顶级证书、服务器证书和客户端证书。 顶级证书是权威证书颁发机构所持有的证书,权威机构使用顶级证书给服务器证书或客户端证书进行签名。 服务器证书是我们最常见的,访问多有 https 的网站,都会收到一个服务器证书,如果证书是有有名的权威机构颁发,则浏览器就会帮我们处理证书。如果证书是由不知名机构颁发,浏览器会跳出窗口,问我们是否信任此证书。 客户端证书常见于网银。像招商银行的客户端,可以使用电子证书。之所以存在客户端证书,原因是服务器想验证客户的合法性。这时候,信任是双向的。 一般互联网上,只需要服务器证书,信任是单向的,服务器并不对客户做验证。 自签名证书 当用于在私人网络或测试环境中时,与公共信任机构无关,我们可以自签名。OpenSSL 自签名证书是由 OpenSSL 库生成的一种数字证书,它用于加密与认证,由证书持有人本身进行签名。这样的证书一般不被第三方信任。 生成私钥 openssl genrsa -out private.pem 2048 genrsa:用于生成 RSA 密钥对的 OpenSSL 命令 -des3:使用 3-DES 对称加密算法加密密钥对,该参数需要用户在密钥生成过程中输入一个口令用于加密。今后使用该密钥时,需要输入相应的口令(服务启动时要输入)。如果不加该选项,则不对密钥进行加密。 -out:密钥保存到的文件 2048:RSA 模数位数,在一定程度上表征了密钥强度。 可以查看密钥: openssl rsa -noout -text -in private.pem 生成公钥 在生成证书过程中其实使用不到公钥文件的。 openssl rsa -in private.pem -pubout > public.pem 数据签名认证测试 这不是证书自签名过程的环节,仅用于测试。 1、私钥签名: echo -n A message for signing > data.txt openssl dgst -sha1 -sign private.pem -out sha1.sign data.txt 2、公钥验签: openssl dgst -sha1 -verify public.pem -signature sha1.sign data.txt 输出 Verified OK 说明签名验证正确。内容没有被篡改过。 自签名 虽然也可以自己扮演 CA 的角色来给证书签名,完成标准的证书申请流程。还有更简单的办法,那就是自签名。 以前生成证书的时候不需要备用名称字段(subjectAltName),现在的浏览器校验证书都需要这个字段。如果没有这个字段,即使证书的 CN(Common Name)与你的域名或 IP 是一致的,浏览器还是会提示证书无效: NET::ERR_CERT_COMMON_NAME_INVALID 所以我们需要配置扩展信息,可以与常规信息放在一起存到一个配置文件: [req] default_bits = 2048 prompt = no default_md = sha256 req_extensions = req_ext distinguished_name = dn [dn] C=CN ST=Guangdong L=Shenzhen O=MCULoop OU=R&D CN=192.168.1.100 #IP或域名 emailAddress=webmaster@localhost [req_ext] #不一定是这个名字,但要与其他地方匹配 basicConstraints = CA:FALSE keyUsage = nonRepudiation, digitalSignature, keyEncipherment subjectAltName = @alt_names [alt_names] #不一定是这个名字,但要与其他地方匹配 IP.0=192.168.1.100 IP.1=如果还有别的就这添加 DNS.0=www.mculoop.com DNS.1=如果有别的域名也可以这样添加 保存成一个文件名(任意),比如:req.conf 如果按照标准的流程,这里应该先生成申请文件: # openssl req -new -key private.pem -out cert.csr -config req.conf 不过我们自签名,就不用申请文件了,直接签名即可生成证书: # openssl req -x509 -key private.pem -days 3650 -out cert.crt -config req.conf -extensions req_ext -days 3650:从生成之时算起,证书时效为 3650 天。 -key:指定证书所使用的密钥文件 -out:生成证书文件 至此就完成自签名证书的生成了。 查看证书: openssl x509 -noout -text -in cert.crt 自此证书就可以使用了。不过由于证书不是被浏览器信任的 CA 机构签的,所以用浏览器打开网站时仍会提示: 您的连接不是私密连接 NET::ERR_CERT_AUTHORITY_INVALID 这时把证书安装到“受信任的根证书颁发机构”存储区就可以了。 自己扮演 CA 完成签名 上面的自签名证书已经可以用了,若要体验一下完整的签名过程可以再扮演 CA 的角色来给申请文件签名。 生成 CA 根证书 其实也是 CA 自签名的过程,先按上面的过程搞出 CA 根证书。 openssl req -x509 -newkey rsa:1024 -nodes -keyout ca.key -out ca.crt -days 3650 ----- You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [AU]:CN State or Province Name (full name) [Some-State]:GD Locality Name (eg, city) []:SZ Organization Name (eg, company) [Internet Widgits Pty Ltd]:MCULoop Organizational Unit Name (eg, section) []: Common Name (e.g. server FQDN or YOUR name) []:MCULoop CA Email Address []: 命令已经包含了私钥文件的生成过程。 用 CA 签发证书 openssl x509 -req -CA ca.crt -CAkey ca.key -CAcreateserial -in cert.csr -out cert.crt -days 3650 -extfile req.conf -extensions req_ext 如果要使用签发的证书,需要把 CA 证书添加进信任机构,签发好的证书就不需要加...
Linux 系统 MySQL 数据库自动备份
备份 MySQL 要用到 mysqldump。mysqldump 是 MySQL 自带的逻辑备份工具,它的备份原理是通过协议连接到 MySQL 数据库,将需要备份的数据查询出来,将查询出的数据转换成对应的 insert 语句,当我们需要还原这些数据时,只要执行这些 insert 语句,即可将对应的数据还原。 mysqldump 的基本用法 mysqldump [OPTIONS] database [tables] 如果你不给定任何表,整个数据库将被导出。 通过执行 mysqldump --help,你能得到你 mysqldump 的版本支持的选项表。 此外,mysqldump 还支持很多其他选项,例如: --all-databases, -A:备份所有数据库。 --databases, -B:用于备份多个数据库。如果没有该选项,mysqldump 把第一个名字参数作为数据库名,后面的作为表名。使用该选项,mysqldump 把每个名字都当作为数据库名。 --force, -f:即使发现 sql 错误,也忽略错误继续备份。 --host=host_name, -h host_name:备份指定主机上的数据库。 --no-data, -d:只导出表结构,不导出数据。 --password[=password], -p[password]:连接数据库使用的密码。 --port=port_num, -P port_num:指定连接数据库的端口号。 在使用 mysqldump 时,你需要根据你的需求选择合适的选项。同时,你也需要确保你有足够的权限来访问和备份数据库。 请注意,mysqldump 生成的备份文件是 SQL 脚本,你可以使用任何文本编辑器打开和查看。当你需要恢复数据时,你可以使用 mysql 命令来执行这个 SQL 脚本,将数据恢复到数据库中。 在使用 mysqldump 进行备份和恢复时,请务必小心操作,避免数据丢失或损坏。建议在正式操作前,先在一个测试环境中进行验证,确保操作的正确性和安全性。 手动备份 $ mysqldump -u root -p 数据库名 > 数据库名.sql Enter password: 直接生成压缩文件: $ mysqldump -u root -p 数据库名 | gzip > 数据库名.sql.gz Enter password: 如何才能做到不输入密码?毕竟要做到定时自动备份总不能到备份时间后人工敲密码吧。 使用 ~/.my.cnf 配置文件: 在 MySQL 用户的家目录下创建一个名为 .my.cnf 的配置文件。例如,如果 MySQL 用户是 root,则文件路径为 /root/.my.cnf。 在该文件中添加以下内容: [mysqldump] user=your_mysql_username password=your_mysql_password 将 your_mysql_username 和 your_mysql_password 替换为您的 MySQL 用户名和密码。 设置文件的权限,确保只有文件所有者可以读取它: chmod 600 ~/.my.cnf 现在,当您以该用户身份运行 mysqldump 命令时(不要带 -p 参数),它将自动从 .my.cnf 文件中读取用户名和密码,而无需手动输入。 定时自动播放 创建备份脚本: #!/bin/sh dbname='数据库名' mysqldump -u root $dbname | gzip > /data/backup/db/$dbname-`date +"%Y%m%d"`.sql.gz 设置文件的执行权限: chmod 755 db_backup.sh 配置定时任务: $ crontab -e 0 1 * * * "/绝对路径/db_backup.sh" 这样就可以每天在凌晨 1 点钟执行备份任务...
Hugo 使用笔记
什么是 Hugo Hugo 是一个用 Go 语言编写的静态网站生成器。它具有简单、易用、高效、易扩展和快速部署等特点。与其他动态网站生成系统(如 WordPress、Ghost 和 Drupal)不同,Hugo 在创建内容时就已经生成了静态页面,这大大优化了网站的访问速度,并提供了出色的写作体验。 Hugo 适用于构建各种高性能的静态网站,特别是博客、文档和个人网站等。它使用简单的 Markdown 和 HTML 等标记语言来创建内容,并使用 Go 语言的模板引擎来自定义主题和布局。这使得用户能够轻松地创建和定制自己的网站。 Hugo 还具有跨平台性,可以在不同的操作系统上运行,包括 Windows、Linux 和 macOS 等。它对于资源消耗较低,不依赖昂贵的运行环境,如 Ruby、Python 或 PHP,也不依赖任何数据库。这使得 Hugo 成为一个轻量级且易于维护的网站生成器。 另外,Hugo 还支持多种部署方式,可以将生成的静态网站部署到各种托管服务上,如 Heroku、GoDaddy、DreamHost、GitHub Pages、Google Cloud Storage、Amazon S3 和 CloudFront 等。这使得用户能够灵活地选择适合自己的托管方案。 在使用 Hugo 时,用户可以通过简单的命令行操作来创建和管理自己的网站项目。Hugo 提供了丰富的命令和选项,使得用户能够轻松地生成、预览和部署网站。 总的来说,Hugo 是一个功能强大、易用和高效的静态网站生成器,适用于个人和小型团队搭建高性能的网站。它提供了优秀的写作体验、快速的网站访问速度和灵活的部署选项,使得用户能够轻松地创建和管理自己的静态网站。 配置环境 Linux Linux 用户可以直接使用包管理器安装 apt, yum, dnf, zypper, pacman 等等, 包名就是 hugo Windows Windows 用户可以在 GitHub 的 release 下载对应的安装包。 在下载安装包时,有 hugo 和 hugo_extended 两种可运行文件下载,其中 hugo 仅支持 js,hugo_extended 是 hugo 的扩展版本,在支持 js 的基础上还支持 ts。 解压后将 hugo.exe 的路径添加到系统环境变量 Path 中。 创建站点 使用命令 hugo new site blog 即可创建一个名称为 blog 的 hugo 站点项目,存放目录就是 blog。如果想在当前目录下创建需要加上--force参数,也就是hugo new site . --force。创建的项目默认使用的是 toml 配置文件,我比较喜欢用 yaml,所以呢,加一个参数:hugo new site blog --format yaml 目录结构: $ tree . -A . ├── archetypes │ └── default.md ├── assets ├── content ├── data ├── hugo.yaml ├── i18n ├── layouts ├── static └── themes 8 directories, 2 files 然后可以从主题站 https://themes.gohugo.io/ 选择一款主题。 下载到的主题解压到 themes 目录(带着主题根文件夹),然后就可以使用了。 配置站点 通常获取到的主题都会提供示例,里边会有配置文件 config.toml 或 hugo.toml,扩展名也可能是 yaml 等。按照这个文件修改我们站点根目录下的配置文件即可。 发布内容 使用以下命令创建一篇内容: hugo new posts/article.md posts:自定义的一个文件夹,当然也可以是别的,或者不指定文件夹都可以。 article.md:文章文件名,运行 hugo 时将会被渲染成一个页面。 生成的文件会被存放到 content 目录中。 自动生成的内容文件: +++ title = 'Article' date = 2024-01-10T00:53:04+08:00 draft = true +++ 这个自动生成的内容是 markdown 的 front-matter,这里是 toml 格式的,也可以手工改成 yaml 格式的。 实际上没必要使用 hugo new 命令创建内容,直接在相应目录手工创建 markdown 文件就行了。这里只是为让大家了解一下标准做法。 需要注意一下这里的 draft 参数,它是草稿的意思。如果发布时没有用 --buildDrafts,那么这个内容是不不会被渲染的。发布时改成 false 即可。 调试站点 如何在本地查看运行效果呢?在站点根目录运行 Windows 命令行 或 git bash 都可以,执行命令: hugo server --buildDrafts ... | EN -------------------+----- Pages | 3 Paginator pages | 0 Non-page files | 0 Static files | 0 Processed images | 0 Aliases | 0 Sitemaps | 1 Cleaned | 0 Built in 4 ms Environment: "development" Serving pages from memory Running in Fast Render Mode. For full rebuilds on change: hugo server --disableFastRender Web Server is available at http://localhost:1313/ (bind address 127.0.0.1) Press Ctrl+C to stop --buildDrafts:构建时包含标记为 draft(草稿) 的内容 默认使用了 --watch 参数,可以在修改文章内容时让浏览器自动刷新 根据提示,在浏览器中打开网址 http://localhost:1313/ 即可查看网页结果。上边创建的那篇内容的 URL 则是:http://localhost:1313/posts/article/ 发布站点 本地调试完后就可以生成静态页面,然后传到服务器了。 生成静态页面的指令 : hugo --gc --minify --cleanDestinationDir --gc:开启在构建之后运行某些清理任务(例如删除无用的缓存文件) --minify:对任何能够支持的输出格式(HTML、XML 等)进行压缩 --cleanDestinationDir: 先删除目标文件夹中的文件,不然改为 draft 的生成文件不会被自动删除 将 public 文件夹中生成的所有文件上传服务...