容器化应用在构建时经常因为下载包而花费很多时间(有时遇到网络抽风,一个版本要多次构建才能成功),而大多依赖包几乎又都是不变更的。于是我有这样一个构建镜像的优化思路:
用一个最小化的容器,将所需的包提前下载到本地,再将这些包构建成一个个的本地仓库。在需要构建的容器中,把软件源替换成本地仓库,就可以节省构建容器的时间(数量级的)。
这里之所以将其记下来,写成博客,还有一个原因:虽然这里用到的全都是现成的工具和软件,但是除了自身的手册和 --help
,其他有帮助的文档实在过于分散,而且据我搜索一圈下来,这些现成的工具总会碰到一些文档中未提及,甚至 Stack Overflow 之类 网站都很少碰到的小“坑”,而解决这些“坑”才是最耗时的。
我之后会在 github 上将其部分开源出来,放在这里(TODO)
目前经过测试兼容的发行版有:
Dockerfile
中。这里需要注意的是,本地的软件仓库一般没有做签名校验或者 https 之类,需要手动添加信任。这里只对较为繁琐的步骤进行说明
|
|
yumdownloader
,前一个方案试过 dnf install --downloadonly
,发现这里的未知的坑不少,其中一个是下载完成后,已经下载到本地的包偶尔会被删掉,感觉是 dnf / yum
本身有一些存储优化策略。--resolve
选项是为了指定让 yumdownloader
下载指定软件包的依赖包--installroot
不推荐使用这个选项来指定下载路径,使用该选项后,软件源配置文件中的宏(变量)都不自动解析了。比如常见的 $releasever
变量,需要额外手动指定。yumdownloader
会直接将包下载到工作目录,直接用 cd 提前切换工作目录即可
|
|
apt-get install --donwload-only --reinstall
来下载包,那么依赖包如果是当前容器中已经存在的包就不会再下载了。如 downloader-container (用于下载的容器) 中已存在 ca-certificates
和 openssl
两个软件包,此时再执行接下来的命令,结果就是:由于 --reinstall
选项 ca-certificates
会被下载,但是 openssl
作为 ca-certificates
的依赖包,就会被忽略了。
|
|
apt-get download
而不是 apt-get --install --donwload-only
,主要原因是在子命令 apt-cache depends
中,查询到的依赖包,会有首选和次选(替代),而这两者往往是冲突的,就算 apt-get install
使用了 --donwload-only
也会导致包下载失败,因为无法解决冲突。下面列举一个 apt-cache depends
的结果,其中 pinentry-curses
是 <pinentry:i386>
的更优先选择。
详细说明可以参见 https://www.thecodeship.com/gnu-linux/understanding-apt-cache-depends-output/
|
|
apt-get download
也是直接将软件包下载到当前目录的,所以提前用 cd
命令切换工作目录即可
|
|
--non-interactive
主要用于脚本中,防止 zypper
等待用户输入直到超时--pkg-cache-dir
用来指定下载目录-f
用来强制下载已经安装的包。这里其实会遇到和 apt-get install --download-only
中一样的问题,就是依赖包如果已经安装,则不会下载。目前我暂时这样写,有缺少的基础包就手动加上了。zypper
要区分 global arguments 和 subcommand arguments,具体到这条命令就是 install 前面为 global arguments,而后面是 subcommand argumentsyum 仓库的目录结构如下:
|
|
说明:yum 仓库的结构比较简单,在发行版子目录 -> CPU架构目录下,存放下载的 rpm 包,然后在同目录下创建本地仓库索引。
创建 yum 仓库索引的命令如下:
|
|
其中,createrepo
还有一个 c 版本的 createrepo_c
,速度会更快,使用方法相同。推荐较新的发行版直接使用,比如 centos 8 / fedora 31+ / amazonlinux
|
|
较新的发行版某些包是用 modularity 1的方式构建的,如果想针对这些包构建本地仓库需要额外的命令:
文档详见:https://docs.fedoraproject.org/en-US/modularity/hosting-modules/
|
|
其中 REPO_NAME
是本地仓库的名字
这里一个值得注意的命令是 repo2module
(来自 https://github.com/rpm-software-management/modulemd-tools),因为在上述文档中并未提及如何生成 modules.yaml
文件。
fedora 或者 centos 8 (需要额外添加 epel 仓库) 可以通过 dnf install -y python3-gobject modulemd-tools
来安装 repo2module
命令
apt 仓库的目录结构如下:
|
|
说明: apt 仓库分 dists/
和 pool/
两个子目录,dists/
子目录下存放索引,pool/
子目录下存放软件包。
创建 apt 仓库索引的命令如下:
这里本地仓库就不再使用 gpg 签名 Release 了,完整命令详见:https://medium.com/sqooba/create-your-own-custom-and-authenticated-apt-repository-1e4a4cf0b864#35dd
|
|
其中 base
是自定义的仓库子目录,这里方便之后扩展。
apt-ftparchive
命令可以通过 apt-get install -y dpkg-dev
安装。
下面的 host.docker.internal
是通过 docker build
的 --add-host
添加的域名,4891 为本地 openresty 监听的端口
|
|
|
|
|
|
CentOS 7
的时候,发生了致命错误,导致系统无法启动。
这些内核的版本号如下:
3.10.0-123
3.10.0-229
这里其实是一个 AMD Family-17h 架构的 CPU 与较老版本的内核之间的一个兼容性 bug, 该 bug 已经在更新版本的内核中修复:
kvm 中的操作系统无法启动后,使用 vnc 连接上该机器,发现报错:
|
|
这个错误我见过很多次,在搜索引擎输入关键字也能找到一堆解决方法。不过大都是 UUID 对不上导致的错误,我这里是 LVM,所以我怀疑是内核启动时不识别 lvm 的分区导致的系统 无法启动。
然后我尝试使用标准分区来代替 LVM,重装系统,报错依旧,无法启动。
这时我开始怀疑 boot 分区的文件系统,重装后的系统使用的是 XFS
,而我印象中很老
版本的内核只支持 ext2 / ext3 / ext4 的文件系统,于是我又重装了两次操作系统,/boot
分区分别使用 ext4
和 ext2
,依然没有解决问题。
这时我又开始怀疑 grub 2 的参数问题,由于在使用标准分区后,指定启动硬盘的语句就变成了
linux16 ... root=UUID=xxxx
,这里的 root=UUID=xxx
是不是也有可能是老的内核版本
不支持的呢?于是我在 /etc/default/grub
文件中加入了 GRUB_DISABLE_LINUX_UUID=true
,
再执行:
|
|
更新之后,再重启试试
…
还是不行
我开始怀疑内核启动后压根没找到设备,于是开始找 kvm 的疑点:virtio。virtio 是 kvm 提供
的硬盘总线 (bus),应该需要对应的驱动才行,于是我执行 lsinitrd
命令来查找是否有
对应的驱动:
|
|
结果是:驱动找得到,和可正常启动的较新版内核没差别。
这时还有一个疑点,就是开机时一闪而过的一句提示(我重启了很多次,最终是截图才看清这句话):core perfctr but no constraints; unknown hardware!
用这个关键词,Google 查了一下,结果豁然开朗:这个是 AMD Family-17h 与较老版本内核之间的一个兼容性问题。可以引用下面这句话解释:
In family-17h, there is no PMC-event constraint. All events, irrespective of the type, can be measured using any of the six generic performance counters.
而这里的 AMD family-17h 对应的是 zen / zen+ / zen2 架构的 CPU,和正在使用的这台 虚拟机母机对应得上,也能刚好解释之前在 Intel 平台上可以正常使用该版本内核启动 机器。
当然这个问题的解决办法就是升级内核,在几年前各大操作系统已经应用了这个修复。
]]>总体下来还是很满意的,虽然在使用过程中确实遇到些问题,也基本 Google 解决了。
当时还打算写一下相关踩坑教程,可惜都搁置了,这篇文章基本能覆盖你碰到的大部分问题:https://mechanus.io/ke-neng-shi-zui-hao-de-yubikey-gpg-ssh-zhi-neng-qia-jiao-cheng/
这篇主要说一下 Yubikey 和 polkit 工具集之间配合的坑:
环境:fedora 30 / 31 (两台虚拟机都遇到过)
插入 Yubikey,非 root 用户执行 gpg2 --card-status
发现没有找到设备,提示:
|
|
执行 journalctl -xe
发现有类似以下的日志:
|
|
仔细分析这个日志可以看出来,这是一个异常调用栈的输出,而这里的症结是 user: 1000
没有 access_pcsc
权限。
通过查看 polkit 文档,我们知道,polkit 是为了使低优先级进程 (pcscd) 和高优先级进程(驱动程序)通信而设计出的权限管理工具集。
想要授予权限,需要新增配置文件 051-org.debian.pcsc-lite.rules
(文件名可自定义,以 .rules
结尾即可) 到 /etc/polkit-1/rules.d/
目录。
其中 050 为默认规则,050 之后为自定义规则,050 之前是需要对默认规则的补充
配置文件如下:
|
|
polkit 配置文件的语法和 JS 十分相似,这里可以使用 polkit.log 来做调试输出,其他的方法和说明在 polkit 文档中说的很清楚1。
调试过程中需要多注意日志,如果出现 Error compiling script
这样的关键字,就说明有编译错误,此次更改不会生效。
在学会自由泳后,小米手环在使用过 8 次之后光荣的出故障了。
于是在 JD 和 TB 上寻找靠谱的替代品:
最终的选择如标题所示,COUNTU 2018
Moov Now 本是首选,不过在看了几篇测评之后,发现其测试精准度依然一般。因为有小米手环的前车之鉴,就 PASS 掉了。
Swimovate 的两款单独列出来是因为价格和功能,在理论上,这两款表的测量精准度达到了专业级别,Live 版多了一个数据导出的功能,但是要配合买一个 USB 数据夹。只考虑测量准确性的话,买 Swimovate PoolMate2 标准版就足够了。待定。
GARMIN Forerunner735 是专业的铁人三项用表了,只是价格不太美丽,PASS。
还剩两款非手环形态的选项:COUNTU 和 SportCount,这两款其实是在全网能买到的指套型计数器中针对游泳的唯二选择。其中 SportCount 的操作和显示据说比较蠢,而 COUNTU 作为一个国产品牌做了很多人性化的小优化。
最终就是在 Swimovate 和 COUNTU 中选择了一个更硬核更便宜的一个:COUNTU。
]]>夏天来了,工作之余打算通过游泳保持日常的运动量。
当然这个选择也不算是破天荒,还在北京工作时,当时的 leader 就申请了游泳卡,带我们在公司旁边的游泳馆每周去一锻炼一次。每每想起,还能回忆起那时游泳完就在公司后面吃点煎饼果子再回去工作的样子。
这时有必要提一下我的游泳经历了:
这里可以看出,我接触游泳很晚,而且去泳池基本就是打个酱油。至今仍然在浅水区学习。
现在的公司说是给报销一定的健身费用,于是我报了家附近的游泳班,接下来就说一下我在游泳班中,以及游泳班后的练习经历。
在游泳馆报了成人班学蛙泳,时间是每周一至周五的晚上七点半到八点半授课。而我计划的学习时间是每周一、二、四的下班后。
第一周,带着陌生感去上课。跟教练说明了自己的水平,在教练面前游了一小段「蛙泳」。教练看后摇摇头:你还是学自由泳吧,你的蹬腿像蝶泳。
于是我就开始了自由泳的学习:
除了在游泳班中的学习,我也在视频网站上寻找些视频教程。我目前在看的就是「梦觉教游泳 – “自由泳入门第二版”(含字幕)」受益匪浅。
其中几个对我比较重要的知识点:
这里解释一下「训练计划」这个知识点,对游泳水平的提高很有帮助:即每次下水前,将本次游泳要进行的训练先规划出来,比如我目前是 200 米打腿训练 + 针对动作训练 + 1.5km - 2km 的配合训练。
这样有计划的训练,比单纯的每次练习配合游或者冲刺游高效得多,也不会太枯燥,使得水平可以有肉眼可见的提高。
目前按照每周两次的频率去游泳,每次 1500-2000 米左右的配合训练。目前由于换气还不够顺畅(会分心),单次游超过 100 米还有困难。但换气的呛水和假呼吸已经得到改善,单次游 50 米已经不再有障碍 :)
]]>Openresty 是一个基于 Nginx 和 Lua 的高性能 Web 平台。其主要组成部分为:
lua-nginx-module
功能相似,区别是 lua-nginx-module
提供的是 nginx 的 http 模块的 API,而 stream-lua-nginx-module
提供的是 nginx 的 stream 模块的 API。lua-resty-redis
/lua-resty-mysql
/lua-resty-http
在基于 Openresty 的开发中,要同时了解 Nginx 和 Lua 的工作原理,再去进一步了解 lua-nginx-module 的原理。这样才能有一个在性能与可维护性之间良好的平衡的项目。
在基于 Openresty 的开发中,首先要认清 Nginx 在整个项目中的角色。典型的角色有如下几种:
考虑到使用 Openresty 开发的项目一般偏向服务端,故在这里只讨论前两种情况
这是 Nginx 的典型用法,即作为网关或者负载均衡的前端,直接接收外部请求,做简单处理后,使用 upstream
功能将流量代理至后端。
|
|
画一个典型的角色框架图。其中将 Openresty 的部分抽象为由 Lua
负责的网关逻辑层面,和由 Nginx
负责的 Upstream
功能的层面。
这里还是着重讲 Nginx 在这种架构中的作用:
ngx_accept_mutex
同步锁的机制来解决该问题。2http
/server
/location
,提供简单的路由功能(当然也可以设置复杂的路由,不过我倾向于将复杂的路由部分交给 Lua 代码来完成)在这种架构中,我一般的做法是:
将简单的路由部分交给 Nginx 配置文件,再将响应用户请求的 Lua 程序通过 lua-nginx-module
提供的挂载点运行起来
|
|
如果这里需要使用现成的 web 框架的话,可能会看到 Vanilla
之类的框架。
我个人还是更倾向于多种轻量级的模块综合:
这样开发出来的 web server 优点如下:
luarocks
工具(其实 openresty 官方提供了 opm
工具来解决 lua-resty-*
的依赖)这里有 Lua 源码的简单解析,就不再赘述了。
lua 源码系列文章:
table.new
来申请已知大小的 lua 表,因为 table.new
调用的 lua_createtable()
函数在 LuaJIT
中是可以被优化的。还有一个优点是预分配了表的大小,防止了表在增长时的资源消耗。lua-nginx-module
或 lua-resty-core
提供的 API 返回的结果缓存。可以通过减少不必要的栈操作来减少消耗。lua
中的 __gc
元表方法在某些模块中的应用,防止由于触发 __gc
而导致的奇特BUG4lua
层面的代码应当避免IO的阻塞:如使用 lua 原生的 os
库读写本地文件、系统调用,这会影响整个 nginx worker 的运行这里主要讲的是 lua-nginx-module 中的一些重要配置。
lua_code_cache: on|off
: 关掉代码缓存的结果就是每个请求运行一个单独的 Lua VM 实例,即对 lua 代码的改动可以即时生效。这个特性建议仅在调试时打开。有些模块的功能可能会依赖于代码的缓存lua_package_path
/ lua_package_cpath
这两个指令直接决定了 nginx 是否能找到你要引用的 lua 模块,所以非常重要其他的指令文档也可以通过 openresty/lua-nginx-module 官方文档清晰的看到。
lua-releng
: 对 luac
命令行的一个封装,将多个 openresty
提供的全局变量纳入正确范畴内reindex
: 主要是针对基于 Test::Nginx
模块下的测试文件的一个语法格式检查luacov
工具结合,在终端生成彩色的代码覆盖率,稍作处理就可以当做 CI 工具链中的代码覆盖率数据来源Test::Nginx
: 是 openresty 官方在用的数据驱动的测试框架使用过的历史版本为: 1.11.7 -> 1.13.6。升级的重要原因是:stream-lua-nginx-module 加入 udp server 的支持 ↩︎
mutex 同步锁:(TODO) ↩︎
在 http 相关模块中的挂载点有: NGX_HTTP_POST_READ_PHASE: /* 读取请求内容阶段 / NGX_HTTP_SERVER_REWRITE_PHASE:/ Server请求地址重写阶段 / NGX_HTTP_FIND_CONFIG_PHASE: / 配置查找阶段: / NGX_HTTP_REWRITE_PHASE: / Location请求地址重写阶段 / NGX_HTTP_POST_REWRITE_PHASE: / 请求地址重写提交阶段 / NGX_HTTP_PREACCESS_PHASE: / 访问权限检查准备阶段 / NGX_HTTP_ACCESS_PHASE: / 访问权限检查阶段 / NGX_HTTP_POST_ACCESS_PHASE: / 访问权限检查提交阶段 / NGX_HTTP_TRY_FILES_PHASE: / 配置项try_files处理阶段 / NGX_HTTP_CONTENT_PHASE: / 内容产生阶段 / NGX_HTTP_LOG_PHASE: / 日志模块处理阶段 */ ↩︎
__gc 元表方法相关问题举例 https://github.com/openresty/lua-resty-lock/issues/20 ↩︎
前几天想实现一个在博客的 front-matter
中加入一个标签来区分生活类文章和技术类文章,在首页的表现为链接的颜色不同的功能。
在 Hexo 官网及 Google 中寻找解决方案的过程中发现了几个问题:
之前在 hexo + gitlab 服务隐藏静态文件 中提到的:
之前有过使用 hexo 的经验,又加上 node 的生态在个人博客这里又异常繁荣(大量node相关的第三方插件可用),还是决定迁移过来了。
其实也是造成问题一的原因之一。关于问题一在 hexo 的 github issue 中也有讨论:Why not totally replace Swig with Nunjucks? #1593,其中 mozilla/nunjucks2 作为 Swig
模板的升级,可以作为 Swig
不再维护后的替代品。但 hexo 项目这边对默认模板的替换就没那么迅速了。3
问题 2 则是由来已久,对主题开发者不友好的程度有点高。这里可以看其中一位主题开发者的感受 https://blessing.studio/get-hexo-posts-by-category-or-tag/ :
今天在将博客主题移植至 Hexo 时,想要获取某个分类(Category)或者标签(Tag)下的所有文章(准确来说是想获得文章总数),在使用中文关键词搜索时,没有获得任何有用的信息(或许是我搜索姿势不对)。换用英文关键词「hexo category all posts」后搜索到了所需的信息,遂决定写一篇文章记录一下,希望能帮到后来人。~~~~
这里不得不吐槽一下,Hexo 的文档真是太烂了,太烂了。写个主题,有时候想要实现一个功能还要疯狂看 Hexo 源码,说不出话。
我的解决方案如下:
swig
模板不变,通过搜索引擎寻找类似的实现方法。nunjucks
插件使 hexo 支持该模板,再实现该功能。我的解决过程正好是按照我列举的顺序来的
由于 hexo 对 front-matter 自定义的部分需要涉及 hexo 引入脚本,而我并不想在 hexo 这个框架本身花费过多时间。于是放弃。
在过程中,我使用了另一种方法:用一个不常用的 front-matter
字段用来标识链接颜色(我使用的是 layout
这个变量)。实际效果也实现了类似的效果,可是代码看上去很容易让人困惑。4
nunjucks
只有几个 hexo 相关插件,hexo-renderer-nunjucks 和 hexo-nunjucks,打开来看就知道这两个项目的最后更新时间都锁定在三年前。于是放弃。
这几个模板引擎直接由 Hexo 官方的支持,然而在实际使用的时候,还是觉得那个过了时的 swig
会稍好一点(主要是符合我对模板的印象)。于是放弃。
先列一下备选项:5
我最终在这些中选择了 Hugo,主要是因为最近正好在学习 Go
。而 Hugo
使用的 “text/template” 也算是一个 Golang
的一个标准扩展模块了,应该不会像 Hexo 那样多模板之间来回跳转^(问题1)^。 Hugo 的官方文档也是肉眼可见的多^(问题2)^。
确定了解决方案,再理一下选择当前解决方案后要做的事:
其中,由于同是 markdown
写的文章,迁移大概就是一条 cp
命令。这里就不详述了。
在模板的部分,基本就是像素级的 COPY:将 swig
模板实现的功能按行级别的使用 text/template
实现。当然语法风格是按照 Hugo 官方文档中的来写。
在过程中还是遇到了几个小难点,这里将解决方案也一并贴出来:
/tags/
页面的时候,需要先将文章按照标签分组,再依次将各组标签中的文章遍历出来。在 text/template
中,变量的作用域非常奇怪,代码如下:
|
|
这一段代码以直觉判断,$v
应该是输出 "changed"
。然而实际结果有点意外。我对此情况的理解:在 text/template
的实现中,每个代码块拥有独立的作用域,这个作用域在遇到嵌套时也不会发生继承。
这样实现起来的代码应该是最干净并简单的。遇到这种情况,官方的建议是使用 .Scratch
来创建一个页面级作用域可读可写的变量,然而这对于主题模板来说有点重了。
在 Google 的帮助下,找到这样一段 gist: https://gist.github.com/Xeoncross/203d8b1459463a153a3c734c98b342a9
|
|
·
出现。在某些文章中,我的小标题是以 <h3>
来标识的,而自动生成目录的模板却没弄自动去掉未使用的 <h1>
和 <h2>
|
|
这在 Hugo 的 Github Issue 中也有体现:Heading levels in Markdown table of contents #1778,而这里又牵涉到另外一个问题,就是 Hugo 使用的 markdown 渲染模板 russross/blackfriday 在 Hugo 中还是 v1 的版本,而 v1 版本解析 markdown 后的输出结果是一段 HTML,因为这个,在生成 .TableOfContents
的时候有这样一段很丑陋低效的代码:https://github.com/gohugoio/hugo/blob/master/helpers/content.go#L416
|
|
在这里是用处理字符串的方法来解析 .Content
中的内容,再将其要生成的内容拼凑成几块,再加到原内容中。这个问题在 blackfriday.v2
中得以解决,即输出一个 AST
再交由其他程序处理,这样也能保证后续版本的兼容性。但在 Hugo 中,作者也是多次推迟该特性的里程碑 Upgrade to Blackfriday v2 #39497。
但是在 Issue 讨论中,各路大神也提出了自己的解决方案,可以点进 Heading levels in Markdown table of contents #1778 中去查看详情。我采用了这其中的模板解决方案:
|
|
这个方案在日后的特性版本合并后可以简单的移除模板中的相应部分。算是比较简便的一种。
模板迁移完成之后,就开始把最初想实现的功能实现出来。在 Hexo 中,front-matter
可以填写用户自定义的字段(文档见:https://gohugo.io/variables/page/#page-level-params)。
我这里选择使用 linkcolor
字段。
|
|
再在主页遍历标题的地方加入该变量的判断:
|
|
我觉得每次写一个抽象的颜色 #7076c7
看起来不好看,又在 data/color.toml
目录中加入以下内容(文档见:https://gohugo.io/templates/data-templates/)
|
|
修改主页模板
|
|
这样在 front-matter
中只需要写 linkcolor: blue
就可以达到相同的效果了。之后想有其他的颜色相关扩展功能,也可以方便的实现。
这里官方文档讲的很详细,我是使用 Netlify
平台发布博客的,文档在 https://gohugo.io/hosting-and-deployment/hosting-on-netlify/
其他常用平台在文档中也有讲到。
这里主要是讲对老博客的提交记录的迁移,我这里使用了将 Hexo 博客的文件及目录放入一个单独目录 hexo_archive
中,将 Hugo 平台的代码放在项目根目录中,这样,之前的提交记录和文件得以保留,又留下了一个对于 Hugo 相对干净的目录。
github 页面 https://github.com/paularmstrong/swig 已经显示:“This repository has been archived by the owner. It is now read-only.” ↩︎
看到 mozilla 这个前缀,感觉 Nunjucks 这个项目应该是会稳定维护一段时间 ↩︎
截止 2019-01-18日,在 hexo 项目中有看到相关的 PR Replace default swig engine with nunjucks #2903。 ↩︎
代码详见:https://github.com/wukra/izhengfan.github.io/commit/2e3d6782b1bf5c43d149fabe961b7fb09c84a2c5 ↩︎
来自 https://snipcart.com/blog/choose-best-static-site-generator ↩︎
在 NexT & izhengfan 结合主题 文章中提到,我把这个主题从 Jekyll 迁移到 Hexo。我很喜欢现在这个主题 ↩︎
截止 2019-01-18日,这个特性本打算在 2017年10月的 v0.31
版本加入,却一推再推,到现在是放在了 v0.55
发布计划中 ↩︎
这里主要采用文字叙述,来试图阐释这个过程,用来梳理知识点和查缺补漏。如果其中的叙述不准确,还请谅解。
Q: 浏览器输入地址敲回车开始,到收到响应为止的过程,阐述其中的细节。
A:
复杂的程序都需要分层。
按照OSI七层协议来讲这个过程,重点是OSI的二层–数据链路层(以下简称MAC层)、三层–网络层(以下简称IP层)、四层–传输层以及应用层中的表现。到达应用层后,讲一下CDN的原理与常用架构。再简单讲一下数据中心的架构。
OSI 的七层模型是一个开放式系统互联的参考模型,而 TCP/IP 协议簇是一组用于实现网络互联的通信协议。 OSI 的七层分别为: 物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。 记忆口诀: All People Seem To Need Data Processing
TCP/IP 的四层分别为:接口层、网际层、传输层、应用层。 其中TCP/IP模型中的接口层对应着物理层和数据链路层,而应用层对应着会话层、表示层、应用层。
每个联网的设备都有一个唯一的硬件地址,即我们常说的 Mac 地址,Mac 地址用于在广播域中寻找对应的设备。Mac 地址虽然有唯一性,但是没有可定位性,所以出了广播域后则使用 IP 协议来进行通讯。
二层设备可以进行Mac地址与端口对应设备关系的缓存,可以缓存同一广播域内的设备。 其中Mac地址与IP地址的对应关系可以缓存在本地路由设备中,称之为路由表。而在已知IP未知Mac地址时,可以发起ARP请求来查询Mac地址。
浏览器中输入的一般是域名,域名通过 DNS 解析为IP。
首先判断对应IP是否在同一网段内,即通过CIDR来判断,若对应IP不在同一网段,则请求通过网关利用路由协议去寻找对应的网络和设备。当经过中间的路由设备(三层)时,先比对Mac地址是否一致,再比对IP地址是否一致,看是否为发给自己的包还是需要转发出去。
路由分为动态路由和静态路由。这里的路由协议即动态路由协议。
基于Bellman-Ford算法, 路由器将部分或全部的路由表传递给与其相邻的路由器
RIP: 1. 使用跳数作为度量值 2. 最大跳数为 15 3. RIP v1 中会定期同步整个路由表
BGP: BGP可以看作一个高级距离向量路由协议。在BGP系统中,可以将网络分为多个自治系统。自治系统内部使用iBGP来同步路由信息,而自治系统之间使用eBGP广播路由。
自治系统:一个(或多个)实体管辖下的所有IP网络和路由器的全体。自治系统由IANA分配一个ASN(自治系统编号),从而在互联网的ISP之间运行BGP协议。
ASN: 一个16位的编号,现在已经有32位的写法:<高十六位的十进制数字>.<低十六位的十进制数字>
自治系统分类: 1. Multihomed As(多出口自治系统):多与一个连接的自治系统。这类系统不允许其他自治系统穿过它去访问另一个自治系统。 2. Sub As(末端自治系统):仅与一个其他自治系统相连的自治系统。 3. Transit As(中转自治系统):一个自治系统通过自己提供将几个隔开的网络连通的服务。这就是 ISP 的实质。
BGP 的使用条件: 1. 需要支持存储大型路由表的路由器 2. 需要多个连接 3. 有足够的带宽传送所需数据(包括路由表)
基于 Dijkstra 算法, 路由器将链路状态信息传递给在同一区域内的所有路由器
OSPF: 1. 使用组播发送链路状态更新,在链路状态变化时使用触发更新,提高了带宽的利用率 2. 无最大跳数限制,且以延迟和开销的作为度量值
IGP 包括了 OSPF/RIP 等协议,是在自治系统内部使用的路由协议,其主要作用是发现与计算路由。 而 IBGP 也是在路由系统内部使用的路由协议。两者的区别是: 1. IBGP 会将路由发现的工作全交给 IGP,而其本身着重于路由的控制。 2. IGP 对于大型路由表的处理能力很差,而IBGP则可以将其分层处理。 3. 若直接将 BGP 的路由信息交给 IGP 处理,会丢失其中的路由属性,从而有路由环路的隐患。而IBGP可以处理这些路由属性(第一条)
第一种,在出网关之后目标Mac地址变为下一跳设备的Mac地址,而来源IP和目的IP始终不变,直到到达指定设备。该方法适用于无冲突IP地址的场景,而在IP地址重叠时就需要另一种NAT网关了。
第二种,即NAT网关,在请求出了网关之后,来源IP和Mac都变为网关的,而目的IP不变,到达下一跳。当响应到达时,再将目的IP和Mac映射回去。
DNS 是一个用来做域名与IP地址转换的分布式数据查询系统。在客户端发起DNS查询之后,会先查询本地DNS服务器,即运营商或路由器上配置的DNS server,如果没有记录则会查询根域名服务器,根服务器会将顶级域名的服务器地址返回,顶级域名服务器将权威域名服务器地址返回。其中客户端向本地服务器发起的是递归查询,而本地dns服务器向上查询则是迭代查询。
在 IP 包头中标识着传输层的协议类型,常见的有 UDP 和 TCP。
UDP 是不面向连接的协议。其继承了 IP 协议的大部分特点:如按包发送、无状态、无序、无拥塞控制。简单来说,就是一个不维护状态的传输协议。在 UDP 的包头中,只有源端口号和目的端口号来标识连接。在简单的环境、内网及允许丢包的场景中可以使用。也可以在应用层实现状态的维护,从而变成一个可靠的连接。
TCP 是面向连接的协议。所谓连接即一系列状态的更替。维护了一个复杂的状态机之后,使连接变得有序、可靠、控制拥塞等功能。但 TCP 的底层 IP 协议是无连接和无序的,所以 TCP 实现这些特性大量使用了重传算法和拥塞控制算法。
三次握手:请求端发起SYN包想建立连接(SYN_SENT),接收端收到SYN包之后返回 ACK + SYN 包(SYN_RCVD),请求端收到 ACK 包之后(ESTABLISHED),再响应对端的 SYN 请求一个 ACK,对端收到 ACK 包,则状态变更为 ESTABLISHED。此时,两端均完成了一次收发包,状态为 ESTABLISHED,连接建立成功。
四次挥手:请求端发起 FIN 包想要终止连接(FIN_WAIT_1),接收端在收到 FIN 包后返回 ACK(CLOSE_WAIT),发起端接收到 ACK(seq=k+1) 则进入 (FIN_WAIT_2),当接收端处理完毕上层逻辑后,返回 FIN + ACK 包(seq=k+1),发起终止连接(LAST_ACK),发送端在收到这两个包之后,响应 ACK 包,并进入 TIME_WAIT 状态,等待两个 MSL 后关闭连接。而接收端在收到 ACK 后也关闭连接。TIME_WAIT 状态是为了防止接收端没收到最后一个 ACK 包,从而发起 FIN + ACK 重试。
看网页一般使用的是 http 协议来传输数据。http 是基于 tcp 应用的协议,在http中请求与响应的报文均为明文。请求报文分为:请求行、请求头、请求体,其中请求行可以细分为请求方法和请求地址。响应报文分为:响应状态、响应头和响应体。其中响应状态分为响应码和原因。
常用的请求方法有: GET
/POST
/PUT
/DELETE
/OPTION
常见的响应码有: 200 OK/201 Created/301/302/403 Forbidden/404 Not Found/405 Not Allowed/500 Internal Error/502 Bad GateWay/503 Service Unavaliable/504 Gateway Timeout
其中 TCP 协议中有 keepalive 概念,在 http 中也有 keepalive 概念且在 http/1.1 后会默认开启。其中 http 的 keep-alive 是允许客户端通过同一个 tcp 连接发送多条请求,而 tcp 的 keep-alive 则是通过心跳来让 tcp 连接保活的一种机制。两者没有直接的联系。
由于 http 的请求和响应报文均为明文,在对安全要求高的场景就不适用了。这时就引入了 https。 https 是使用非对称加密交换密钥,交换密钥后使用对称加密的基于 http 的协议。双方进行握手的流程是这样的:
几乎在我开始待业的同时,我就开始关注租房子的事情。大体要求是:
按照这样的要求找了三天,有三个备选项:
最终在各种取舍下,选择了房屋C。
回顾整个搬家的过程,正好用了一周时间。
周一:租车将简单衣物和床垫床单及清洁用品搬来。将床垫床单晾晒,备用。 周二:在家中整理物品,待搬。 周三:将锅碗瓢盆等易碎物品搬来。 周四:将部分小件物品搬来。将沙发罩清洗晾晒。办理宽带移机。 周五:预约搬家公司,将所有物品打包整理。家具清空。房屋C这边打扫干净并消毒。 周六:上午跟随搬家公司将物品搬至房屋C。下午则日租了共享汽车,将剩余物品分三次搬往新家。期间将衣柜与收家具的奸商谈好价格,并在第二次搬家的间隙预约师傅将电视挂在墙上。三次搬完后,已经是凌晨1点多了。 周日:上午又搬了一次厨房用品和冰箱食物,将衣柜售出。更换浴室淋浴头,洗澡休息。晚上吃了波龙,收工。
租房方面:租房子要求明确的话,快速筛选出两三套合适的,然后在其中做取舍。 开车方面:考完驾照后其实很少开车,自己又胆怯。但这次搬家,开车往返七趟,并在小区的严酷环境中穿梭。开车时的胆怯已经不见,甚至在拥挤的车潮中,还会听广播来放松了。可喜可贺。 整理方面:多准备纸箱,如果是楼梯房的话需要大小适中,不然一个沉甸甸的箱子搬上楼,会腰酸背痛。
搬家方面: 在观察搬家小哥的过程中,突然体会到工作的重要性。
我自己之前也搬了几次东西上下楼,对比起搬家小哥的效率,差了一大截。
一方面是各种工具的运用,还有就是身体素质的不同。只有「工作」这个概念才能把这样一批专业的人聚集在一起,而在重复工作的过程中,又会总结出许多提高工作效率的技巧。
负责开货车的师傅呢,只是负责把车开进开出,及操作货箱升降,并不去搬家。
工作分工的不同,可以使人们在垂直领域有更多时间深耕,避免了学习精力被分散。
]]>也是想给各位跳槽的朋友敲个警钟,如果你要跳槽,哪怕多讨厌现在的工作,都不要随便跳进其他工作里。一定要明白自己为什么跳,思考自己想要的是什么。 如果确实想走,又确实没有找到适合的,宁愿裸辞,贫穷一小会儿,也不要让自己进入一个新的坑。因为新的坑,会让你感到更绝望,同时也破坏了自己的职业稳定性。
希望我能在这潮水中保持初心,等到真正合适自己的【坑】。
在这里列一下假期中在做的事:
还望忽略其中塔罗牌的部分,这也是我在浏览时目光自动跳过的部分 ↩︎
哈哈,十三年前的日志是回忆当时三年前的事。所以就算是为了这种酷,也要坚持写博客啊。
我在不同博客平台1 之间辗转过多次,也因为域名主机等问题放弃维护了一些文章,而目前可查的文章居然是 2013-2014 年在 github 上留底的 jekyll 搭建博客期间的文章。算一算竟也是 5年前了。
动手把文章迁移过来,以增加博客的历史厚重感,可以在博客的页脚看到 since 2013
了 :)
Wordpress -> jekyll -> bitcron -> hexo ↩︎
目录
想要真正的多色彩支持,需要 模拟终端
+ tmux
+ vim
三重的支持。这里说明下我的编程环境:windows 虚拟机(Ubuntu 18.04) + SecureCrt + Tmux + Vim 8.1。
于是我要做的就是在这几点上分别做True-Color支持。
我在Windows上偏爱使用的模拟终端是 SecureCrt,但,我在查阅 True-Color 支持列表时https://gist.github.com/XVilka/8346728#now-supporting-truecolour却发现 SecureCrt 不在支持之列。在试了几个Windows支持的虚拟终端之后,最终选定了 MobaXTerm
。
至于原因,无外乎其与 SecureCrt 相似的操作逻辑,以及提供 Personal Edition。
至于配色偏好,我依然选了墨绿色背景的 solarized 主题。2
这里采用网上常规的方法没有行得通,经多方搜寻,找到了 http://lists.gnu.org/archive/html/emacs-devel/2017-02/msg00635.html 更改 terminfo 使得 tmux 支持 Tc (True-Color) 的方法。(这里我采用了git上最新的 tmux 源码编译的 tmux)
常规方法,更改 $HOME/.tmux.conf
文件:
|
|
更改 Terminfo 方法(实测有效):
|
|
校验 True-Color 的方法:
|
|
效果图:
这里要改的点主要有三个:
.vimrc
文件
|
|
solarized8
主题
|
|
hi Visual gui=reverse
这个反色指令,于是手动调整了被选中文字的配色。
|
|
(依然在 .vimrc 文件中修改,具体位置则是紧接着主题配色后面)
before:
after:
这里同样是选中 358-363 行
在使用过程中,还发现了 vim 8.1 中支持的 termdebug 与 true-color 的一个冲突:在 packadd termdebug
之后做了 set termguicolors
的操作的话,在实际调试代码时,正在执行的行就没办法高亮了。而正确的做法则是将 packadd termdebug
放在 set termguicolors
之后。
由 256 色支持到 16万色支持,字面意义上就是颜色过渡更平滑了。而在 vim 中,就是代码看着更舒服了(不知道这里的代码颜色高亮有没有相应的True-color扩展)
8-bit color 又名 256 color, 24-bit color 又名 true color,一共有 16,777,216 colors ↩︎
与这款配色的渊源要回溯到 https://blog.csdn.net/zklth/article/details/8937905 这篇文章了,可惜文章中的图已经挂了大半,还好我有本地保存的 Evernote 版。 ↩︎
偏头痛(英语:Migraine)是一种出现反复轻度或重度头痛的慢性疾病,通常伴有各种自主神经系统症状。偏头痛的英文“Migraine”一词源于希腊语ἡμικρανία(hemikrania),意为“头部一侧的疼痛”,其中,ἡμι- (hemi-),意为“一半”,κρανίον(kranion),意为“颅骨”。
通常这种头痛为单侧性质(仅涉及一侧头部),并伴有搏动,可持续2-72小时。相关症状可能包括恶心、呕吐、对光线更加敏感、对声音更加敏感,且肢体活动会加重疼痛的感觉。三分之一的偏头痛患者均能感到病症先兆:短暂的视觉、感觉、语言或肢体障碍都意味着头痛即将发作。 (引用自 https://zh.wikipedia.org/wiki/%E5%81%8F%E9%A0%AD%E7%97%9B)
利扎曲坦 (英语:Rizatriptan,或译为利扎曲普坦)是一种曲坦类5-HT1激动剂,由默克药厂为治疗偏头痛而开发,商品名Maxalt。 (引用自 https://zh.wikipedia.org/wiki/%E5%88%A9%E6%89%8E%E6%9B%B2%E5%9D%A6)
在不定期发作的 “不适”1,如头痛、胃痛、畏光、胸闷气短、鼻炎发作等的轮番轰炸下,我几乎每个月要去医院检查一次身体。当然这些检查无外乎神经内科、耳鼻喉科、眼科、消化内科的常规检查。在这些检查后,医生也会开对应症状的药,有些医生甚至还会开 “中成药”2。而在一次次的发作后,我也感到了这些方法的无力。
医院之外的互联网上,维基百科中,也看到了对偏头痛症状的描述,把我的症状与之对应发现其重合度较高。而对于非医学专业的我,自然不会因为一段描述而对自己确诊,因这有可能是一种 “疑病症”3。
在一次次的就医过程中,在某医生的建议下,得以知道某个眩晕学专家的就诊时间,而最终也是专家确诊了偏头痛。在医生的建议中,提到了国际上认可的偏头痛治疗药物:利扎曲坦。虽然当时医院中并没有该处方药,可在我的请求下,医生还是把药品名称写在了纸上,供我查找。
又一次借助互联网的情况下,在某东医生上挂了号,并开具了处方,买到了利扎曲坦。
在最近一次的偏头疼发作时(已经出现了症状,但还较轻),服用了利扎曲坦,在不排除安慰剂效应的情况下,症状消失。
]]>yinwang.org
1 的超简主题,看起来就像某种高级存档。在找了相关的 hexo 主题 mickeyouyou/yinwang
2 并试用了之后,发现并不满意(主要不满意字体,排版和代码高亮)。
而 Hexo 用户基本都知道 NexT
主题,定制功能强大,找到适合的排版、字体高亮、第三方插件很容易,可无奈太多人在用了,而且显得有些重。
在搜索 vim termdebug
问题时,无意中发现了 https://fzheng.me 的博客,其极简的思路和漂亮的字体让我特别想试用一下相关主题。去 github 上看了相关源码是 Jekyll
的模版,花了点时间转换成 hexo
的模版,在转换的过程中,顺手把几个 NexT
主题中用的顺手的扩展功能移植过来了(因为只有三个页面模版,工作量很小)。
移植过来的几个功能是:
主题项目地址是:https://github.com/wukra/izhengfan.github.io.git
王垠博客地址:http://www.yinwang.org/ ↩︎
仿王垠博客主题的项目地址: https://github.com/mickeyouyou/yinwang ↩︎
|
|
该 perl 代码运行的输出结果大概是:
|
|
即,/etc/hosts
的全部文件内容。
其中,难理解的是第二行,在 perl 的常规写法中,读取文件的完整内容应该是用一个 while 循环逐行读取:
|
|
这里详细的来解释一下第二行:
do { <block> }
是 perl 中的代码块,函数的返回结果即 <block>
最后一个语句。<FILEHANDLE>
是上述 do { <block> }
中的最后一个语句,即返回结果。而在 perl 中,<FILEHANDLE>
的返回结果根据不同的上下文,有两种不同的返回结果,即:
|
|
$/
变量的设置为该语法糖的关键。 $/
变量是 输入的分隔符 (input record seperator),默认情况下是换行符。即 <FILEHANDLE>
在标量上下文时,返回单行文件内容的默认表现是由 $/
变量决定的。而 local $/
相当于 local $/ = undef
local
和 my
的区别:my
是创建一个新变量,而 local
则是临时改变一个变量的值(在作用域内)整体解析下来就是,通过 do {}
创建一个临时的作用域,在作用域中改变 $\
的值,改变了 <FILEHANDLE>
在标量上下文中的分隔符(由 \n
变为 undef
),达到了读取整个文件内容的目的。
DNS 的区域 (zone) 由两部分组成:Resource Records (RRs) 和 Directives
|
|
$
开头的,其中 $ORIGIN
和 $INCLUDE
是在 RFC 1035 中定义的,而 $GENERATE
则是由 BIND
提供的非标准指令。$TTL
指令需要出现在第一个 RR 之前这里的消息是指在 Resolver
和 DNS
系统之间的消息协议。
格式如下:
|
|
(图自 RFC-1035)
|
|
(图自 RFC-1035)
|
|
(图自 RFC-1035)
说明:每个请求通常只有一个 Question Section
,但其实可以通过 QDCOUNT
来指定任意个数的 Question Section
no. of chars
domain name
no. of chars
domain name
…
其中 no. of chars
为相邻 domain name
字符串长度
例:
|
|
(图自zytrax.open)
RR
的 TYPE
x'0001
代表 IN or Internet
Answer / Authority / Additional Section / RR 都采用相同的格式
RR / Answer 格式:
|
|
(图自 RFC-1035)
1
(与 label 格式区分,由于 lable 格式最大值限制为 63),OFFSET
位的值为相对于信息开始位置的偏移量。其中 0 代表着 ID 的第一位。
|
|
(图自 RFC-1035)
x'0001(1)
: A 记录x'0002(2)
: NS 记录x'0005(5)
: CNAME 记录x'0006(6)
: SOA 记录x'000B(11)
: WKS 记录 – Well Known Source 用来描述互联网上的使用特定协议(如TCP(6)
) RFC1010的通用服务(如 SMTP
)x'000C(12)
: PTR 记录. A 记录与 AAAA 记录的反向记录(IP指向域名)x'000F(15)
: MX 记录. SMTP
的 Agent
用来收件的域名x'0021(33)
: SRV 记录. RFC 2782 MX 记录是其特殊情况。SRV 记录是用来被其他特定服务使用的记录字段(如 OpenLDAP)x'001C(28)
: AAAA 记录. ipv6 地址Internet
Chaos
|
|
- Primary NS: 主 NS 服务器. 长度可变. label/pointer/混合
- Admin MB: 管理员邮箱. 长度可变. label/pointer/混合
- Serial Number: 序列号. 32位无符号整型. 格式为 "YYYYMMDDnn"
- Refresh interval: 刷新间隔. 32位无符号整型. 二级NS服务器检查 zone file 的更新的间隔
- Retry interval: 重试间隔. 32位无符号整型. 当主 NS 服务器无法连接时,重试间隔.
- Expiration Limit: 过期限制. 32位无符号整型. DNS resolver 可缓存时长,对于某些 DNS server 来说则是对 resolver 响应的缓存时长
- Minimum TTL: 32位无符号整型. 字段意义取决于 NS 的实现,有以下三种可能:
- NS 对该域名最小缓存时长,几乎没有服务器这样使用(官方遗弃)
- 默认的 TTL 值。(无 TTL 记录时使用该值)
- 定义了当该域无记录时,缓存的时长(区别于有记录时缓存的时长 `TTL`)[RFC 2308](https://www.ietf.org/rfc/rfc2308.txt) (官方推荐)
- MX:
|
|
- PREFERENCE: 优先级. 值越小优先级越高. 一般使用 `0`(max)作为邮件服务器记录,使用 `10` 用来验证域名的所有权
- Mail Exchanger: 提供服务的域名. 长度可变. label/pointer/混合
- A: 32位无符号整型,IP地址
- AAAA: 16个八进制,IPv6地址
- PTR, NS: 地址. label/pointer/混合
Authority: (res only) 在请求中该字段值为 0. 格式同 Answer
. 数据一般为 NS
类型的 RR
Additional: (res only) 在请求中该字段值为 0. 格式同 Answer
. 理论上,任意类型的 RR
都是合法的。实际上,此字段用于提供在 Authority Section
中提到的 NS
域名所对应的 A
或者 AAAA
记录
注:
其中 (res only)
代表着仅在DNS响应中有效的字段
参考文章:
https://www.oschina.net/code/snippet_563463_19381
中看到一个使用 Perl 正则对 Lua 文件中进行自动缩进的脚本,感觉思路不错,单文件就可以做到简单的自动的缩进判断。
可是在 github 上搜索了半天,也没能找到相关的 lua standalone 项目代码。
这里顺便吐槽一下 LuaRocks
,如果要使用 LuaRocks
安装好的脚本,机器上就得安装 LuaRocks
才行,于是就算打包的时候调用 LuaRocks
将依赖解决好,也是不可用的,除非在依赖项中写上 LuaRocks
。对我这种 standalone 洁癖来说非常不友好。
这里我对 Perl 版本的自动缩进改写成了 Lua 版的,其中的正则使用的是 openresty 中的 ngx.re
,启动时要调用 resty
命令行或者在 openresty
中使用 (两种启动方式)
由于之前一直托管博客的网站 https://bitcron.com/ 无法更新证书,而该网站的 Let’s Encrypt 自动脚本又挂掉不能用了(这网站该不会凉了吧(小声)。我决定抽空把博客迁移到纯静态博客上。
之前有过使用 hexo 的经验,又加上 node 的生态在个人博客这里又异常繁荣(大量node相关的第三方插件可用),还是决定迁移过来了。
在 Github
被微软收购后,虽然其CEO直接升级成为 Billionaire,但我对他的好感降到了冰点,目前仅留了几个开源的小项目和之前fork的项目在上面,私有库的付费计划已经停止了。现在把大部分代码及私有库都转移到了 gitlab 上。于是使用 gitlab pages + hexo 的初步技术选型就已经定下来了。
首先,跟着 hexo 官方的文档,一步一步安装,将大的框架搭起来。再配合 GitLab CI 来自动生成及部署静态页面。一切看起来异常的简单。
在 gitlab 上的 pages 选项中,很容易就找得到绑定域名和给域名设置证书的选项。在vps上安装 Let’s Encrypt的命令行工具 certbot
,
按照官方的文档:
|
|
下一步,下一步,直到停在需要验证网站所有权的地方:
访问 http://www.ogura.io/.well-known/acme-challenge/JDbJQP50-rI3LgKGLy7U7EhOkSxr73P0bP2UeTIh1yE
得到指定内容的静态文件。
在自己的 VPS 上做静态文件服务再简单不过了,不过如果是在 gitlab + hexo 的情形下又该怎么做呢。
经过试验得知,在 source/
文件夹下的文件会被直接扔到 public/
文件夹下,但特殊情况是,.well-known
文件夹是个隐藏文件夹,
hexo generate
在生成静态文件的时候,会忽略隐藏文件和隐藏文件夹。
gitlab pages 中举例了一个 jekyll 中的解决方案是使用一个 xxx.md
的文件,其内容为:
|
|
但是在 hexo 中(起码是 next
这个主题下,没有 null
这个 layout),而且生成的链接中带有 .html
后缀,无法通过校验。
折腾了半天自定义layout,也没弄成功。
最终在 .gitlab-ci.yml
中,加了一行手动 copy 的命令,也就成功了。(折腾了半个小时layout模版= =)
|
|
结论以 lua 5.1.5 版本(ubuntu 官方发行版)在 ubuntu 18.04 的实际运行为准
本篇文章使用 tt2 技术,
并使用 Template::Tools::tpage 提供的命令行 tpage
来生成 lua 代码
其中 262144 = 2^18^
|
|
生成的 lua 代码 gist 链接(文件较大): https://gist.github.com/xiaocang/cd947e57cdb6d16b83d7bdc9c4d0cecd#file-too_much_constant-lua
|
|
TODO
其中 32895 = 2^15^ + 2^7^ - 1
|
|
生成的 lua 代码 gist 链接(文件较大): https://gist.github.com/xiaocang/99a5b636694bab0fd870c17ef97fa472#file-control_pattern_too_long-lua
|
|
TODO
|
|
生成的 lua 代码 gist 链接(文件较大): https://gist.github.com/xiaocang/baf52c6e7fffa31d684ad55ddfc47867#file-too_much_upvalue-lua
|
|
TODO
|
|
生成的 lua 代码 gist 链接(文件较大): https://gist.github.com/xiaocang/f4fd0b56bbdec00e4794e661b0ffd994#file-variables_in_function-lua
|
|
from lua5.2.2 src/lparser.c
line 30-32
|
|
自建图床的方案可以说不是很多了,先是用过 Lychee – 使用世界上最好的语言(PHP)开发,使用下来感觉功能太重了:一堆功能,能用上的没几个。还要装一堆图像处理的插件,去做图片缩放(并用不到
目前是使用的 uppy,优点是部署方便,一个 npm install
就解决了,后端用的是 tusd
的 go 版本,部署更是方便,一个二进制文件扔上去就好了。
在选择 uppy 之后,看了半天官方文档,也没有提到怎么样把 uppy 部署好,我甚至以为 uppy 是 uppy server 的后端。走投无路的时候去扒 uppy 的官方例子的页面,发现部署 uppy 只需部署一个静态页面即可,所有选项都在页面的 <script>
标签内写好就成。
示例页面:
|
|
效果图:
功能很简单:上传/生成链接
]]>自建图床的方案可以说不是很多了,先是用过 Lychee – 使用世界上最好的语言(PHP)开发,使用下来感觉功能太重了:一堆功能,能用上的没几个。还要装一堆图像处理的插件,去做图片缩放(并用不到
目前是使用的 uppy,优点是部署方便,一个 npm install
就解决了,后端用的是 tusd
的 go 版本,部署更是方便,一个二进制文件扔上去就好了。
在选择 uppy 之后,看了半天官方文档,也没有提到怎么样把 uppy 部署好,我甚至以为 uppy 是 uppy server 的后端。走投无路的时候去扒 uppy 的官方例子的页面,发现部署 uppy 只需部署一个静态页面即可,所有选项都在页面的 <script>
标签内写好就成。
现拟引入 openresty 做代理web前端,基于 openresty 的 timer 对后端的 http 服务(以及多种网络服务)做监控,再收集信息对 etcd 服务器统一发起服务注册及保持心跳。
示例图
项目地址:https://github.com/xiaocang/lua-resty-etcd-discovery-client
]]>lua 源码系列文章:
lua源码欣赏的第六章内容,与 lua 协程相关
Contents
从C层面来看,Lua的状态就是一个 lua_State
,而在同一个Lua虚拟机中,多个 lua_State
共享一个 global_State
。其中 lua_State
不应当被看为一个简单的静态数据结构,而是一个lua “线程” 中的状态机,其中保存着当前"线程"的执行状态、数据栈、调用栈等信息。
Lua 的数据可以被分为值类型和引用类型。在 lstate.h
中使用联合体 Value
来表示。
|
|
可以看出引用类型使用一个指针 GCObject
来间接引用,其他值类型都是直接保存在联合体中。 为了区分联合体中的类型,还需绑定一个额外的类型字段。
在Lua中,数据栈的空间为 2 * LUA_MINSTACK
,而在 Lua 的 C 调用中,数据栈大小只有 LUA_MINSTACK
这么大。(LUA_MINSTACK
默认为 20)
所以在Lua的C调用时,需要显示的扩展栈大小,使用 ldo.c
中的 luaD_growstack
函数来扩展数据栈大小,其中每次调用至少扩展一倍的大小。在扩展数据栈时,值类型的数据可以直接复制,而引用类型的数据需要调用 correctstack
来修正
Lua 中的调用栈在一个 CallInfo 结构体中,以双向链表的形式存储在"线程"对象里。
|
|
可以看到,在Lua 5.2实现中,调用栈被封装成一个可以无限扩展的堆栈,而仅在GC时清理掉无用的链表节点。
Lua 作为一门嵌入式语言,为了实现不与硬件绑定的协程,在中断和异常处理时统一使用 C 的 longjmp
机制,而当嵌入 C++时,则使用 try / catch
机制来实现。这些是通过宏来切换的。
Lua 中的 pcall 是用函数而非语言机制实现的,实现pcall 是在 c 层面的堆栈来保存和恢复状态。
在纯 Lua 函数调用中,一般不涉及C函数的调用。其过程为:
是生成新的CallInfo,修正数据栈,然后把字节码的执行位置跳转到被调用的函数开头。而Lua的return操作则做了相反的操作,恢复数据栈,弹出CallInfo,修改字节码的执行位置,恢复到原有的执行序列上
在 Lua 底层API中,分为 luaD_precall
和 luaD_poscall
,原因即是:
luaD_precall
来指定字节码的执行位置,而在 luaD_poscall
时执行字节码,并恢复状态。luaD_precall
即可。在 TValue 中采用了 NaNTrick 的技巧来节省内存(Lua 5.2特性),即:根据 IEEE754 中的说明,指数位全为 1 尾数位全为 0 (0xfff8000000000000
) 用来表示这并不是一个数字。它用来表示无穷大,以及数字除0的结果。而当双精度浮点数大于 0xfff8000000000000
情况下,则可以认为这是刻意造出的数字,可用于特殊用途。
NaNTrick则利用这种情况,在32位机器上,将一个双精度浮点数的尾数位(52位)用于储存除数字类型外的其他类型(32位)和值类型(int)足够使用。这样每个值可以节省4个字节的内存。
float
double
]]>https://github.com/xiaocang/lua-5.2.2_with_comments/releases/tag/data_n_call_stack
lua 源码系列文章:
lua源码欣赏的第六章内容,与 lua 协程相关
Contents
从C层面来看,Lua的状态就是一个 lua_State
,而在同一个Lua虚拟机中,多个 lua_State
共享一个 global_State
。其中 lua_State
不应当被看为一个简单的静态数据结构,而是一个lua “线程” 中的状态机,其中保存着当前"线程"的执行状态、数据栈、调用栈等信息。
Lua 的数据可以被分为值类型和引用类型。在 lstate.h
中使用联合体 Value
来表示。
|
|
可以看出引用类型使用一个指针 GCObject
来间接引用,其他值类型都是直接保存在联合体中。 为了区分联合体中的类型,还需绑定一个额外的类型字段。
lua 源码系列文章:
lua源码欣赏的第五章的内容,是与 lua 闭包相关的内容
Contents
在 Lua 中,将一组数据绑定到特定函数上,即为闭包。被绑定的数据成为 upval,也可以成为非局部变量(区别于局部变量和全局变量)。
Lua 的函数区别于 C 中的函数,Lua 的函数可以在虚拟机运行时动态生成,可以在 Lua 的函数中生成函数。将 Lua 的函数原型与一组 upval 绑定,即 Proto
结构体与 UpVal
绑定在一起,就是 Lua 中闭包的实现方式。
Lua 中有两种闭包:Lua 闭包与 C 闭包。其中 Lua 闭包较为复杂。
luaF_close
来关闭,并将之前的指针指向一个安全的地方来存放。C 闭包相对与 Lua 闭包较为简单,由于C 闭包不会引用外部的 upval,upval都为关闭状态。将 upval 绑定在 Closure 结构体上即可。C 闭包还有一种无 upval 的特殊情况,这种情况称之为轻量函数(C light function)。轻量函数不需要使用 UpVal 结构体,也不需要 GC 来管理。
在 Lua 源码中,有用到 UNUSED()
宏,是为了避免编译器未使用的警告,源码如下:
|
|
]]>https://github.com/xiaocang/lua-5.2.2_with_comments/releases/tag/lua_closure_06
lua 源码系列文章:
lua源码欣赏的第五章的内容,是与 lua 闭包相关的内容
Contents
在 Lua 中,将一组数据绑定到特定函数上,即为闭包。被绑定的数据成为 upval,也可以成为非局部变量(区别于局部变量和全局变量)。
]]>lua 源码系列文章:
lua源码欣赏的第4.2.2章到第四章结束的内容,主要还是与 lua 表相关的源码。
Contents
对于数字类型的哈希,其中说到了几个版本迭代改进的事情。
Lua5.1 中直接采用读取内存中相应块的数据相加来进行哈希,这种算法在64位系统下可能会出现相同的数字哈希结果不同的BUG:
在64位系统下,long double被认为是16字节而不是32位系统下的12字节,以保持对齐。但 其数值本身还是12字节,另4字节被填入随机的垃圾数据
Lua5.2 则改进了这种算法,变成了用户可配的两种算法。其一是利用联合体来计算数字的哈希(条件是:数字类型为 double, 目标机器使用 IEEE574 标准的浮点数),其二是性能不高,采用更为通用和标准的哈希算法。
表的迭代是在 lua 中经常使用的部分,在仔细阅读了源码之后,理解了之前在网上看到的 lua 查询表结构是否为空的原理。
首先说明一下 lua 中对表长度的定义:t[n] 为非空,而 t[n + 1] 为空,则 n 为表的长度。再补充一点,当表中的数组部分被填满后或者为空的时候,也会计算哈希表部分的长度。
还有之前没注意到的一点,在进行表迭代操作时,其他语言都是禁止对该表进行任何操作的。而在 lua 中,在迭代表过程中,可以对已经存在的值进行修改甚至删除(赋值为 nil),而不会影响迭代的结果。
对正在迭代表进行删除元素操作的时候,如果刚好碰到 gc 过程,被设置为 nil 的键值对会被标记为死,而 findindex
函数中对这种情况进行了处理。
如果对正在迭代的表进行新建操作时,则可能会使这个新的key不被遍历到,或者新建操作触发了 rehash
动作,则会导致重复遍历等问题。
lua 中的 next(table [,index])
函数是接受两个参数的,我在判断一个table是否为空时都是这样用的:
|
|
Lua 实现复杂数据结构,大量依赖给 table 附加一个元表( metatable)来实现
于是对原表的操作是 Lua 中的热点,对热点的优化必要性不言而喻。
从 Lua 创建表开始,这些元方法就直接附加在了 lua 的表中,而利用 Table
结构中的 flags
位对元方法进行标记,也可以大大提升元方法的查询速度。在实际查询中,直接使用相应的位,直接就可以查询到相应的元方法。
在 Lua 创建原表时发现,Lua 中使用 luaS_fix
对状态机中的数据结构进行标记,当标记为 FIXEDBIT
时可以使该结构不被回收。
]]>https://github.com/xiaocang/lua-5.2.2_with_comments/releases/tag/lua_table_metatable_04
lua 源码系列文章:
lua源码欣赏的第4.2.2章到第四章结束的内容,主要还是与 lua 表相关的源码。
Contents
对于数字类型的哈希,其中说到了几个版本迭代改进的事情。
Lua5.1 中直接采用读取内存中相应块的数据相加来进行哈希,这种算法在64位系统下可能会出现相同的数字哈希结果不同的BUG:
在64位系统下,long double被认为是16字节而不是32位系统下的12字节,以保持对齐。但 其数值本身还是12字节,另4字节被填入随机的垃圾数据
Lua5.2 则改进了这种算法,变成了用户可配的两种算法。其一是利用联合体来计算数字的哈希(条件是:数字类型为 double, 目标机器使用 IEEE574 标准的浮点数),其二是性能不高,采用更为通用和标准的哈希算法。
]]>lua 源码系列文章:
lua源码欣赏的第四章到4.2.1章的内容,主要看了与 lua 表相关的源码。
Contents
一个lua的表结构,最多由三块连续的内存组成:一个 Table 结构,一个存放了连续整数索引的数组,一块大小为2的整次幂的哈希表。而每一部分的大小,在调用 rehash
函数时,会由 computesize
函数在保证利用率在 50% 以上的前提下重新分配。
哈希表使用哈希的闭散列的冲突解决方法,在发生碰撞后会将碰撞的两个key用单链表链接起来。
而在表的查询过程中,也遇到了长字符串的惰性哈希计算。这点优化的原因,云风在书里也有所说明:
在大多数应用场合,长字符串都是文本处理的对象,而不会做比较操作,内部唯一化处理将带来额外开销
但字符串优化是在 Lua 5.2.0 之后才加入的新特性,是否在 openresty 中配套的 luajit 中也有,先打个问号。
]]>https://github.com/xiaocang/lua-5.2.2_with_comments/releases/tag/lua_table_04
lua 源码系列文章:
lua源码欣赏的第四章到4.2.1章的内容,主要看了与 lua 表相关的源码。
Contents
一个lua的表结构,最多由三块连续的内存组成:一个 Table 结构,一个存放了连续整数索引的数组,一块大小为2的整次幂的哈希表。而每一部分的大小,在调用 rehash
函数时,会由 computesize
函数在保证利用率在 50% 以上的前提下重新分配。
哈希表使用哈希的闭散列的冲突解决方法,在发生碰撞后会将碰撞的两个key用单链表链接起来。
]]>
Wiki原文 https://en.wikipedia.org/wiki/Vienna_Development_Method
维也纳开发法 (VDM) 是历史最悠久的计算机系统开发的 形式方法 1 之一。其原型是在20世纪70年代于 IBM 维也纳实验室完成的,后来 VDM 成为了一门基于 VDM-SL (VDM Specification Language) 的一系列技术和组件的形式规范化语言。VDM 还有扩展的形式: VDM++。VDM++ 支持面向对象和并发系统。对 VDM 的支持包括商业和教育中用于分析模型的工具,还包括模型的测试和验证和由VDM模型生成的程序代码。VDM及其工具在工业应用历史和形式主义研究中不断发展,为关键系统、编译器、并发系统和计算机科学逻辑的工程化做出了显著的贡献。
在VDM-SL中,计算系统可以在比编程语言实现更高的抽象层次上进行建模,从而可以在系统开发的早期阶段对设计进行分析和确定关键特征和缺陷。已经验证的模型可以通过细化过程转化为详细的系统设计。该语言有一个正式的语义,可以在更高抽象层次上证明模型的属性。 它还有一个可执行子集,使得模型可以通过测试进行分析,并可以通过图形界面展示,以便模型可以由不熟悉建模语言本身的专家进行评估。
]]>Wiki原文 https://en.wikipedia.org/wiki/Vienna_Development_Method
维也纳开发法 (VDM) 是历史最悠久的计算机系统开发的 形式方法 1 之一。其原型是在20世纪70年代于 IBM 维也纳实验室完成的,后来 VDM 成为了一门基于 VDM-SL (VDM Specification Language) 的一系列技术和组件的形式规范化语言。VDM 还有扩展的形式: VDM++。VDM++ 支持面向对象和并发系统。对 VDM 的支持包括商业和教育中用于分析模型的工具,还包括模型的测试和验证和由VDM模型生成的程序代码。VDM及其工具在工业应用历史和形式主义研究中不断发展,为关键系统、编译器、并发系统和计算机科学逻辑的工程化做出了显著的贡献。
]]>
|
|
这里还没画出这个结构中的 Model 部分,因为也是一团乱。
简单的说明下这个被滥用的结构。
路由主要分为两部分,一部分是通用功能的接口路由,一部分是用户定制的接口路由。这是比较理想的情况:在用户没有定制功能时,就直接使用通用路由的接口;而在需要定制功能的时候,就单独在定制路由中加新的路径。
在实际使用中,发现这并不算是通用解决方案。因为有些用户在使用通用通用接口时,有时会有定制化需求,如:
Common Route
部分,要经过一个鉴权的中间件来确保合法性。在这里又有客户定制的鉴权方案要做。Model
里又创建新的表或者字段,而这个定制字段又不能很好的做访问控制。总之,这个架构中在设计之初,有些模块或者架构就是固定的,而在实际应用的时候,几乎要求每个模块都可以被定制和替换(用户级别)。
lua 源码系列文章:
读完风云关于 lua 的字符串部分,对 lua 的字符串处理也有相应的理解。
Contents
lua 的字符串在 lua 内部分为长字符串和短字符串,而这两种特性在 lua 层面是透明的,这种分类应该只是lua 解释器内部的优化。
补充1: 其中短字符串的最大长度由 LUAI_MAXSHORTLEN
宏决定。长字符串调用 createstrobj
来创建。
长短字符串主要在字符串创建、比较时做不同的处理。
字符串创建时,短字符串直接进行内部化,并且在 TString
结构体中的 extra
位上标记是否为内部保留字段。而长字符串在创建时,是直接进行内存拷贝,在 extra
位上进行标记,以在比较或者内部化时进行惰性哈希。
字符串比较时,短字符串直接对比指针地址。而长字符串则进行逐字符串比较。
补充2: 长/短字符串的优化是从 Lua 5.2.1 开始加入的,在那之前所有的字符串都是保存在全局的哈希表中。
补充3: 在字符串拼接时,使用 ..
是低效的,这其中涉及申请内存以及内存拷贝。建议使用 table.concat
4 或者 string.format
5
在 lua 中还存在一种 UData
的结构,这叫做 userdata,这种数据结构主要是在 lua 在与 c 和 c++ 交互时,将 c 的数据结构交给 lua 的 gc 处理。
具体来讲,在C中可以使用 lua_newuserdata()
会在 lua 中创建一个数据结构,并将指针返回。这和 malloc
的调用差不多,但区别是你不用手动调用 free
来释放内存,而交给 lua 的 gc 即可。
一个好的例子是 lua 的 io
库,其中将 FILE *
的 C 数据结构放入 lua 的 userdata 中,通过实现一个 __gc
的元方法,可以实现将文件句柄在 gc 时自动关闭。
https://github.com/xiaocang/lua-5.2.2_with_comments/releases/tag/lua_string_02
参考文章:
2018-11-01 补充 ↩︎
2018-11-01 补充 ↩︎
2018-11-01 补充 ↩︎
table.concat
底层拼接字符串的方式也是使用运算符.. ,但是其使用算法减少了使用运算符..的次数,减少了GC,从而提高效率。主要思路:采用二分思想,用栈存储字符串,新入栈的字符串与下方的字符串比较长度,大于则使用运算符..拼接成新字符串,并移除栈顶的字符串,不断向下直至遇到长度更大的字符串或者栈底,这样保持最大的字符串位于栈底,栈呈现金字塔的形状,最终在使用运算符..将栈中的字符串拼接成最终的字符串。
作者:AaronChanFighting
来源:CSDN
原文:https://blog.csdn.net/qq_26958473/article/details/79392222 ↩︎
string.format
的每个 %s
占位符,有 512 个字符的限制 ↩︎
开散列
实现的哈希表,而在 lua表中的哈希部分,则是使用闭散列方法。
其中开散列和闭散列都是哈希表的实现方式,
两者的特点分别是:
而关于开散列和闭散列等的具体算法表述就详见原文吧。
原文地址详见: http://blog.csdn.net/u014613043/article/details/50726630
]]>开散列
实现的哈希表,而在 lua表中的哈希部分,则是使用闭散列方法。
其中开散列和闭散列都是哈希表的实现方式,
两者的特点分别是:
项目地址在: https://github.com/xiaocang/perl-balancer
TODO: 暂时还没有 XS 版的实现
]]>其中 phpUnit
的文档在 https://phpunit.de/manual/current/zh_cn/index.html
|
|
ex:
|
|
数据库部分在官方文档中很详细 主要的assertion就是检查数据库中是否存在某个条目,是否不存在某个条目,以及软删除的检查
|
|
官方文档中在这部分只给出了少量的例子,我在实际应用中,有更多有用的用法,如 Log 对象:
|
|
Guzzle 作为发起 HTTP 子请求的重要模块,在 laravel 中的应用也应当被测试用例覆盖到。 Guzzle 提供自己的 Mock 方法:
|
|
扩展用法:想具体测试子请求中的输入与输出,可以写一个简单MVC类,输入预期子请求的输入(url, method, header, body),来动态生成例子中的 $mockResponses
数组。
在 laravel 项目的目录下,有一个 phpunix.xml
文件,在其中可以指定在测试运行时的一些环境变量。
|
|
我在项目中强制指定了测试时使用的数据库地址与名称,为了防止在生产环境中有人运行该脚本,在 .env 文件的设定下,对线上的数据库进行操作。
当然在官方的配置说明中提到,在测试运行时,如果有 .env.testing
的文件存在,在使用 --env=testing
参数限定下,.env.testing
会将 .env
文件覆盖
运行测试用例非常简单,只要在项目目录下执行 phpunit
文件即可。
|
|
phpunit
还有一些参数可供使用,其中我主要使用的是 --filter
,用来执行某个或者某批测试用例。
|
|
其中 phpUnit
的文档在 https://phpunit.de/manual/current/zh_cn/index.html
lua 源码系列文章:
Contents
刚刚读完 风云关于lua 的内存管理部分的理解,自己也对相应部分代码做了阅读和注释。 lua的内存管理主要特点如下:
https://github.com/xiaocang/lua-5.2.2_with_comments/releases/tag/lmem_01
而lua的全局状态机这部分则是因为牵涉后面的部分太多,如 gc, string, table 等,只能看懂一点。 其中需要重点理解的部分是:
]]>https://github.com/xiaocang/lua-5.2.2_with_comments/releases/tag/global_state_02
lua 源码系列文章:
Contents
刚刚读完 风云关于lua 的内存管理部分的理解,自己也对相应部分代码做了阅读和注释。 lua的内存管理主要特点如下:
这两种设计方案都是分别接近极端的做法,其中我主要负责的 1类接口工作量大的可怕,每新增一个需求我就要进行上千行的改动,这对我来说算是“可怕”级别的劳动量,而接口实现在别人看起来又是简单且没技术含量的,工期又是极短的。
于是我开始准备在现有两种接口的方案中找一个折中方案。(未完)
]]>lua 源码系列文章:
在 openresty 项目中使用 lua 是一件很享受也很纠结的过程。lua 的极简设计让我两年前仅用几天搭建出来一套 CDN 控制系统,而在业务系统渐渐成型的情况下暴露出问题。 如今摆在我面前的解决方案有两个:
如果要使用状态机,则可能极大的复杂化系统的核心部分,导致后期无法维护。 而要使用沙盒,问题是我对lua的底层实现一知半解,在看到风云的Blog后, 我决定抽出时间将lua的源码阅读一遍,加深我对lua的理解,在阅读中我也希望能想出 openresty 项目的理想 lua 架构。
在这里附一下云风的 Lua 源码欣赏
我会在这里更新一些阅读 Lua 源码后的感想和学到的知识。
这是我在 github 上对lua-5.2.2 源码加入中文注释后的项目地址: https://github.com/xiaocang/lua-5.2.2_with_comments
]]>lua 源码系列文章:
在 openresty 项目中使用 lua 是一件很享受也很纠结的过程。lua 的极简设计让我两年前仅用几天搭建出来一套 CDN 控制系统,而在业务系统渐渐成型的情况下暴露出问题。 如今摆在我面前的解决方案有两个:
如果要使用状态机,则可能极大的复杂化系统的核心部分,导致后期无法维护。 而要使用沙盒,问题是我对lua的底层实现一知半解,在看到风云的Blog后, 我决定抽出时间将lua的源码阅读一遍,加深我对lua的理解,在阅读中我也希望能想出 openresty 项目的理想 lua 架构。
]]>addcp.com
的域名续费了1,并且没有败其它域名。于是这个博客不用迁移也不会死了2。-w-
]]>批注为 2018-10-10 添加
|
|
但是在转码过程中遇到了问题,单独的编码输出的.264
文件再加上mp4的封装都可以正常播放。但是将.264
文件合并为一个文件之后,快进时播放就会花屏。
|
|
在重新转码多次之后依然得到相同的结果,说明单纯的append file是导花屏的原因。
在Google之后终于发现是由于x264默认会对输出的.264
文件头部进行优化,导致合并.264
文件的时候出现错误。只要简单的加上--stitchable
这个选项就可以避免花屏了。
|
|
结果如下:
|
|
诚邀您参加由世纪互联运营的 Windows Azure beta 免费试用
我很幸运的得到了Windows Azure beta的试用VPS,而且也没有说多久过期,看来起码可以免费用上三个月以上了2。
当然这个VPS是在国内的网络里,搭梯子
什么的当然做不到。但是还是可以搭个博客什么的。
于是我把在github Pages3上跑的Jekyll博客也移了过来(起码写博客不用开虚拟机,直接登到VPS上就可以了)。
在Windows Azure创建虚拟机的时候我选了CentOS(主要是工作中也在用),然后下一步,下一步。。这样一个虚拟机就创建完毕。
然后打开一个ssh终端,输入在刚才创建虚拟机时候填的主机名、端口、用户名、密码,然后登录到虚拟机上。
登入之后第一件事第一件事就是编辑/etc/yum.conf
文件:
|
|
把其中exclude=kernel*
注释掉,不然装包的时候会报错的。
由于Jekyll需要Ruby环境,所以在终端执行下面的命令,安装Ruby
|
|
Jekyll安装则按照Jekyll官方文档上说的,在终端执行:
|
|
但是我在执行这条命令时发现,gem迟迟没有反应。再执行:
|
|
这里发现,没有反应是因为gem的源被国内的网络环境弄的无法连接(你懂的)
果断按照http://ruby.taobao.org/上的文档,删除了现在无法连接的源,更新了淘宝的源。
|
|
然后再执行:
|
|
果然很快很顺利。^_^
写在后面
不过我现在还是不清楚,能不能把没备案的域名解析过来。。。所以我还是在用那个又长又难看又难记的xxxxxx.chinacloudapp.cn
4。。=w=
]]>批注为 2018-10-10 添加
抄
才是用Bootstrap的精髓。
批注为 2018-10-10 添加
打开这个网站,虽然样貌大改,但依然是当年的那一个。 ↩︎
之前连微信都没有的日子2终于一去不返,不过这是一件让人惊讶的事嘛?
]]>批注为 2018-10-10 添加
git pull origin master
更新代码的时候有时会遇到有冲突无法合并的情况,而又知道在remote端的代码是对的,只要覆盖掉本地的代码就好了。
之前我是这样做的:
|
|
然后也会得到一个我想要的结果。
可是今天当我重复同样的操作时,事故却发生了。很多有用的数据在这个操作之后被删除,而且无法找回了。
我的Git环境是这样的:
这是一个在线上运行的系统,会在同目录下实时生成很多历史数据,然而由于其数据量较大,就没有加到git追踪文件里。但是又没有写好.gitignore
文件。又由于是多人维护的项目,不知什么时候被人执行过一句git add --all
。
就这样。。。珍贵的历史数据在一个不可撤消的git reset --hard
操作之后被删除了。
数据最后也没有找回来。。。1
所以在git管理项目时要注意几点:
git reset --hard
.gitignore
文件批注为 2018-10-10 添加
这是因为我操作失误导致的一个迄今为止最大的线上故障,丢了运营的历史数据。到现在我还是对此引以为戒。 ↩︎
进行一次提交之后,马上发现代码里可能有一两行需要改动,更改之后再次提交,然后又觉得哪里不妥。。。
|
|
发现一些代码需要小改动
|
|
第N次发现
|
|
这样多次之后,git的commit记录就会变得很乱,一堆小改动,却占用了很多次提交记录:
|
|
这时如果使用git rebase来解决这个问题就非常容易了
git rebase语法是这样的:
|
|
这里我们使用到的是-i
选项,也就是交互的rebase
|
|
其中HEAD~3
是该分支的前三次提交。也可以在后面加<branch>
的参数指定要衍合的分支。
|
|
其中,pick
是将该次提交原样提交
reword
也是提交,不过在提交之前会让你再次修改该次提交日志
edit
和reword
差不多,但是在修改提交日志后,会等待你再次git commit --amend
,也就是等待你追加一些需要提交的文件与改动。之后再使用git rebase --continue
完成整个rebase
squash
是将该次提交合并到前一次提交里,但会将这次提交的日志内容合并到上一次里
fixup
则和squash
不同,它会自动忽略此次日志的内容。这正是我此次commit整理中需要的
如上,我就会得到一个这样的日志:
|
|
这样整个世界就都清净了
注意
如果看过git的官方文档的人都应该知道
不要对任何已上线的部分使用衍合
因为这样对线上的提交纪录进行改变,对一个多人协作的项目来说,是不好的。
项目中的其他人会因此感到困惑,因为本来存在的一个提交纪录突然不见了
所以衍合主要是在提交到线上之前进行commit合并,使提交纪录变得干净的方法
]]>]]>批注为 2018-10-10 添加
往年都会因为假期结束而有点小伤感1
今年刚刚毕业的我还是会有点小伤感虽然不知道为什么2
]]>批注为 2018-10-10 添加
<!-- more -->
来截断输出的方法
Google了一下,果然很快就找到相关插件:
<!-- more -->
的插件不过如果你的Jekyll也是托管在Github上的话,那么就不能用插件的方法了。
出于安全考虑,Github在运行Jekyll的时候用了--safe
的参数,第三方插件通通无效。
正当我有点小失望的时候,找到了这篇文章:Post excerpts in Jekyll2
只要利用Liquid模板语言中的一个filter就可以实现:
|
|
然后在截断的文章后加上阅读全文
|
|
这样,即使是托管在Github上的Jekyll也能截断输出了。3
]]>批注为 2018-10-10 添加