分类目录归档:技术

Perl 常用内置函数 -r -e 等

-r: File is readable by effective uid/gid.
-w: File is writable by effective uid/gid.
-x: File is executable by effective uid/gid.
-o: File is owned by effective uid.

-R: File is readable by real uid/gid.
-W: File is writable by real uid/gid.
-X: File is executable by real uid/gid.
-O: File is owned by real uid.

-e: File exists.
-z: File has zero size (is empty).
-s: File has nonzero size (returns size in bytes).

-f: File is a plain file.
-d: File is a directory.
-l: File is a symbolic link.
-p: File is a named pipe (FIFO), or Filehandle is a pipe.
-S: File is a socket.
-b: File is a block special file.
-c: File is a character special file.
-t: Filehandle is opened to a tty.

-u: File has setuid bit set.
-g: File has setgid bit set.
-k: File has sticky bit set.

-T: File is an ASCII text file (heuristic guess).
-B: File is a "binary" file (opposite of -T).

-M: Script start time minus file modification time, in days.
-A: Same for access time.
-C: Same for inode change time (Unix, may differ for other platforms)

参考文献

什么是 WEB3 以及其发展概要

要问何为 Web3,就先从什么味 Web2 和 Web1 来说起吧。

最近在网上看到 Web3 的概念很火热,比肩区块链、元宇宙,其实貌似一整个未来,特意整理一下认为比较好的材料分享出来。

WEB 的进化

https://imagehost-cdn.frytea.com/images/2021/12/31/image83ab2e1e696f8c3e.png

截止目前,互联网技术大致进行过2次重要的革新,每个时代,都有其显著地时代特征。

WEB1 – 信息投喂

虽然我很不喜欢这个词,但貌似事实如此。

早在上世纪 90 年代互联网刚刚开始普及的时候,用户与网络的互动处于非常早期的阶段,即用户只能被动地浏览文本、图片以及简单的视频内容,网站提供什么,用户查看什么。几乎没有互动可言。

上世纪 90 年代中后期,在谷歌、百度发展之前的一段时间,互联网曾经由 AltaVista 和 Netscape 主宰。当时,互联网的存在只是为了宣传他们的实体公司。这些网站是「只读网站」,意味着你只能搜索和阅读信息。

https://imagehost-cdn.frytea.com/images/2021/12/31/image83876cc0f4e5dc0a.png

而那个时代的电子商务网站实际上跟现在超市发的促销手册一样,只是给你展示内容,你无法在网站上下单。

这就是 Web1.0,缓慢的 Web 1.0,完全受控制的 Web 1.0。

WEB2 – 网上互动

当前我们所处的这个时代,大致可理解为 WEB2.0

这一代互联网被称为「Web2.0」或「读写」网络。现在,用户不仅仅是单纯的访问者,他们还可以创建自己的内容,并将其上传到网站上。以著名的视频博主「办公室小野」为例,她仅用了 2 年的时间就从一个默默无闻的视频作者变成了月收入数千万的视频大 V。

https://imagehost-cdn.frytea.com/images/2021/12/31/imagec1f47771a6aaaafa.png

有影响的网红只需要发一个视频就可以让一个餐馆排满长队,也可以用一句话让一家网店差评如潮。互联网人对于互联网的影响力与日俱增,以往网站给用户投喂信息的时代已经过去。

Web2.0 的主要目标是,使互联网更加民主,并尽可能地方便用户访问。

WEB3 – 未来互联网

至于什么是 WEB3 还很难有一个准确的定义,但可以总结这么几个关键词:

  • 统一身份认证
  • 数据确权与授权
  • 隐私保护与抗审查
  • 去中心化运行

在 WEB3.0 时代,身份认证有一个统一的平台,不再需要记录各个平台的账号密码,也不需要担心账号泄露;数据的确权与授权统一规范,不再是一家独大;个人隐私可以受到很好的保护,网站再也不能随意的跟踪用户行为;一切网络服务的运作,都是去中心化的。

至于具体改变了什么,未来将会是什么样子,可以想象。举个例子,某网盘再也不能随意的封禁用户数据,不可能因为新浪博客单方面的停止运营导致我们的博客内容无法访问,音乐资源不再局限于哪一家的版权,因为一切都是去中心化的。

当然改变越大,风险就越大,难度也越大。

怪不得最近总是看到有人说:不学习加密技术是自我毁灭。

就像是有些人已经成了比特币首富,有些人连什么是比特币还不清楚。一些人已经在闷声发大财,有些人还在固有的道路上艰难前进。

变化

在将来,人们的使用习惯和生活方式可能会发生一些变化,比如现在常用 Chrome,未来可能使用 Brave。

https://imagehost-cdn.frytea.com/images/2021/12/31/image3dae6010ffd8d459.png

信息就是金钱,随着 Web 3.0的出现,新的平台将以一种不被垄断服务提供商扼杀的健康竞争水平出现。

这个概念是这样的: 目前,分散的应用程序、钱包、平台以及其他构成 Web 3.0的数字资产是分散的。访问这些接口需要单独的种子、登录和身份标识,这与现有的 Web 2.0非常相似。将通过一个种子将这些不同的平台连接在一起。因为这将作为一个加密的钥匙,可以与其拥有者联系起来,艾森蒂亚将提供身份证明,但不会透露任何非必要的个人身份。

总结

也许 WEB3 没有一个清晰地样子,只是不断的畅想未来的模型。如果一切明了,大概就是下一个时代的变革点了。因此抓住它,不要嫌弃它的简陋,要看到它就是未来。

参考文献

国内对象存储/CDN服务价格横评

话不多说,直接上表格。

价格表(国内HTTPS价格)

名称 存储免费额度(GB) 存储费用(元 / GB / 日) 流量免费额度 流量费用(元/GB) CDN流量费用
多吉云 10 0.003 20 0.11 0.11
七牛云 10 0.098-0.145 \ 0.29 0.28
腾讯云 \ 0.099-0.118 \ 0.5 0.21
阿里云 \ 0.12 \ 0.25-0.5 0.24
又拍云 \ 0.0043 \ 0.5 0.29
百度智能云 \ 0.119-0.15 \ 0.25-0.49 0.20

注:价格整理于2021年12月30日;存储指对象存储,流量指对象存储公网流出流量。

分析说明

由于本人使用过国内各种主流云厂商云存储、CDN服务,目前稳定存储2G左右,每日流量1G左右,使用过各家。知道最近发现多吉云也推出了自家云存储服务,虽然是阿里云、腾讯云包了一层,但费用便宜很多,应该是走量优惠比较大。

后来我就把自己网站的对象存储和CDN都迁移到多吉云了,目前使用起来感觉良好。

还记得上次过年期间,使用的七牛云存储出了点问题,提工单半天没解决,非说是我解析有问题。后来自愈了,对七牛云的印象就没有多好了,因此这次果断迁移到多吉云。

经过这次迁移,发现各家云存储费用差异还比较大,顺便整理,方便各位有需要的客官挑选。

参考文献

Ubuntu 安装 GitKraken 并汉化

GitKraken 是一款超好用的 git 可视化(gui)工具,但是官方不提供多语言支持,今天就以 Ubuntu 平台为例介绍一下安装方法。

安装及汉化

首先在 GitKraken 官网下载安装包:

如果是 debian 系操系统可下载其中的 deb 版本,之后安装即可。

至于汉化,使用 Github 上的 k-skye/gitkraken-chinese 这个仓库即可完成。

方法很简单,安装好之后,使用汉化语言包替换原有的语言包即可。

# 首先备份官方语言包
mv /usr/share/gitkraken/resources/app.asar.unpacked/strings.json /usr/share/gitkraken/resources/app.asar.unpacked/strings.json.bk
# 从github拉取资源并替换
wget https://github.com/k-skye/gitkraken-chinese/raw/master/strings_8.1.1.json -O /usr/share/gitkraken/resources/app.asar.unpacked/strings.json

# 如果在国内访问 github 有困难
# 可使用 fastgit 提供的 github 国内镜像拉取
wget https://hub.fastgit.org/k-skye/gitkraken-chinese/raw/master/strings_8.1.1.json -O /usr/share/gitkraken/resources/app.asar.unpacked/strings.json

之后重启 GitKraken 即可。

https://imagehost-cdn.frytea.com/images/2021/12/30/image031b80c4bef3b2af.png

参考文献

深挖 docker 默认网络 | 为什么 docker 默认网络能上外网

为什么默认配置创建出来的 docker 容器可以访问外网,为什么监听对应端口就能对外暴露docker服务,一张图搞清楚。

首先 Docker 有四中网络模式, 分别是 Bridge、Host、Container、None,默认使用 Bridge,今天就来讲讲 Bridge。

https://imagehost-cdn.frytea.com/images/2021/12/28/drawf92673ecda5420ba.jpg

创建的容器默认使用 bridge 的方式联网,因此默认就可以docker间互通,该网桥名叫 bridge0,通过 nat 的方式与物理网卡相连,每创建一个该模式下的容器,就自动创建一对 veth-pair 挂上去。

# brctl show 
bridge name bridge id       STP enabled interfaces
br-1061a9012b5a     8000.0242998bddbb   no      
docker0     8000.0242f9c2fd4f   no      veth4fc1b46
                            veth86c0817
                            vethe510127
vmbr0       8000.b496916544ad   no      enp59s0f1

通过 nat 的方式,docker可以自由的通过宿主机网卡访问外网,如果映射端口,也是通过 nat 的方式将对应流量送入docker:

# docker ps
CONTAINER ID   IMAGE          COMMAND              CREATED        STATUS        PORTS                                       NAMES
398907d00b97   001b9ea10452   "/sbin/init start"   5 hours ago    Up 5 hours    192.168.226.140:8206->8006/tcp              hci-nos3
475b07e8faa4   001b9ea10452   "/sbin/init start"   5 hours ago    Up 5 hours    192.168.226.139:8206->8006/tcp              hci-nos2
e9955db2132c   001b9ea10452   "/sbin/init"         29 hours ago   Up 29 hours   0.0.0.0:8106->8006/tcp, :::8106->8006/tcp   hci-nos
# iptables -n -L -t nat
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination         
DOCKER     all  --  0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL

Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         
DOCKER     all  --  0.0.0.0/0           !127.0.0.0/8          ADDRTYPE match dst-type LOCAL

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination         
MASQUERADE  all  --  172.18.0.0/16        0.0.0.0/0           
MASQUERADE  all  --  172.17.0.0/16        0.0.0.0/0           
MASQUERADE  tcp  --  172.17.0.2           172.17.0.2           tcp dpt:8006
MASQUERADE  tcp  --  172.17.0.3           172.17.0.3           tcp dpt:8006
MASQUERADE  tcp  --  172.17.0.4           172.17.0.4           tcp dpt:8006

Chain DOCKER (2 references)
target     prot opt source               destination         
RETURN     all  --  0.0.0.0/0            0.0.0.0/0           
RETURN     all  --  0.0.0.0/0            0.0.0.0/0           
DNAT       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:8106 to:172.17.0.2:8006
DNAT       tcp  --  0.0.0.0/0            192.168.226.139      tcp dpt:8206 to:172.17.0.3:8006
DNAT       tcp  --  0.0.0.0/0            192.168.226.140      tcp dpt:8206 to:172.17.0.4:8006

以上面几个docker为例,分别直接监听和指定ip,会发现创建了对应的规则在 iptables 中。

至此,为什么docker访问外网及访问docker的原理讲解完毕,有问题欢迎留言。

参考文献

理解网桥、集线器、交换机、路由器的本质

一直弄不清楚这几者的区别,加之现代的三层交换机,就更搞不清楚几者的关系了。

后来发现试图以现代产品的思维理解这几个概念是极其容易混淆的,必须回归到几者最本质的样子,再去拓展才行。

后来在知乎上找到一段描述,感觉说的很好,在这里记录一下:

  • 集线器
    • 工作在物理层(L1)。一个口收到的信号,原封不动的发送给所有其他的口,由其他的口上的设备自己决定是否接收信号。
  • 网桥
    • 工作在数据链路层(L2)。以太网中,数据链路层地址就是mac地址,网桥与hub的区别在于,网桥会过滤mac,只有目的mac地址匹配的数据才会发送到出口。一个bridge指的是一个输入到一个输出的桥接。
  • 交换机
    • 工作在数据链路层(L2)。早期的switch,其实可以看成多个bridge的集成设备,因此也工作在数据链路层。一个交换机口的输入到另一个交换机口的输出,可以认为是一个bridging。交换机中的MAC table,实际是为了bridge能工作而存在。
  • 路由器
    • 工作在网络层(L3),基于IP地址做转发。

回归本质,有助于更好地理解。至此我终于理解了什么是网桥。

参考文献

Python 实战项目解决循环依赖问题

问题重现

在一次简单的代码合并后,发现 Python 项目跑不起来了,报错如下:

ssh://root@192.168.226.76:22/usr/bin/python3 -B -u /tmp/pycharm_project_882/mimic_daemon_server/helper_main.py
Traceback (most recent call last):
  File "/tmp/pycharm_project_882/mimic_daemon_server/helper_main.py", line 1, in 
<module>
    from mimic_daemon_server import create_app
  File "/tmp/pycharm_project_882/mimic_daemon_server/__init__.py", line 14, in 
<module>
    from mimic_daemon_server.nodes import nodes
  File "/tmp/pycharm_project_882/mimic_daemon_server/nodes/__init__.py", line 2, in 
<module>
    from . import route
  File "/tmp/pycharm_project_882/mimic_daemon_server/nodes/route.py", line 5, in 
<module>
    from mimic_daemon_server.guest_common import ReplicationConfig
  File "/tmp/pycharm_project_882/mimic_daemon_server/guest_common/__init__.py", line 3, in 
<module>
    from .abstract_config import AbstractConfig
  File "/tmp/pycharm_project_882/mimic_daemon_server/guest_common/abstract_config.py", line 16, in 
<module>
    import mimic_daemon_server.qemu_server.Drive
  File "/tmp/pycharm_project_882/mimic_daemon_server/qemu_server/__init__.py", line 25, in 
<module>
    from .QemuConfig import QemuConfig
  File "/tmp/pycharm_project_882/mimic_daemon_server/qemu_server/QemuConfig.py", line 12, in 
<module>
    from mimic_daemon_server.guest_common import AbstractConfig
ImportError: cannot import name 'AbstractConfig' from 'mimic_daemon_server.guest_common' (/tmp/pycharm_project_882/mimic_daemon_server/guest_common/__init__.py)

进程已结束,退出代码1

由于我在 Python 领域还是个初学者,没有遇到类似问题,但是根据分析,问题应该是出在了 循环依赖。

a------->b-------->c
         ^         |
         |         |
         +----d<---+

报错分析

何为循环依赖呢?首先要搞清楚一点,python模块是天然的单例类,就是说第一次导入时会产生一个实例,后面再导入都会直接返回该实例。

模块的初始化时被导入时进行的,就是说每一次 import 都会执行 __init__.py 内的代码。

一旦 Python 的模块导入规划不合理,造成了多个互相依赖的模块均在第一次进入时开始实例化,就会产生 循环依赖 问题。

总结一下,Python 模块是可以循环依赖的,但是必须避免循环实例化。

解决方法

解决循环依赖的方法也有很多:

(1)打破循环实例化态

如果想快速解决,在报错日志中找到循环实例化的两个模块,打破其循环实例化的状态即可。

例如观察文首的一段日志,会很明显的发现调用栈中前后引入了同一个模块,很容易定位到问题。

(2) 强制指定实例化顺序

可以在整个项目的根模块中的 __init__.py 中将所有子模块依赖一遍,调整可能出现循环依赖的顺序,就可以避免后期模块间依赖时产生不可预知的循环依赖问题。

也可以在模块中提供一个 init 方法,在需要的地方手动控制实例化顺序,避免导入时意外的循环依赖。

总结

Python 模块间可以随意引用,但是必须注意实例化的前后顺序,否则就会出现这种循环实例化问题,让人摸不着头脑。

参考文献

解决 perl: warning: Setting locale failed.

使用 Ubuntu 主机远程 PVE 服务器执行命令时报错:

# qm list
perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
    LANGUAGE = (unset),
    LC_ALL = (unset),
    LC_ADDRESS = "zh_CN.UTF-8",
    LC_NAME = "zh_CN.UTF-8",
    LC_MONETARY = "zh_CN.UTF-8",
    LC_PAPER = "zh_CN.UTF-8",
    LC_IDENTIFICATION = "zh_CN.UTF-8",
    LC_TELEPHONE = "zh_CN.UTF-8",
    LC_MEASUREMENT = "zh_CN.UTF-8",
    LC_TIME = "zh_CN.UTF-8",
    LC_NUMERIC = "zh_CN.UTF-8",
    LANG = "en_US.UTF-8"
    are supported and installed on your system.
perl: warning: Falling back to a fallback locale ("en_US.UTF-8").

查看字符集配置发现有报错:

# locale
locale: Cannot set LC_ALL to default locale: No such file or directory
LANG=en_US.UTF-8
LANGUAGE=
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC=zh_CN.UTF-8
LC_TIME=zh_CN.UTF-8
LC_COLLATE="en_US.UTF-8"
LC_MONETARY=zh_CN.UTF-8
LC_MESSAGES="en_US.UTF-8"
LC_PAPER=zh_CN.UTF-8
LC_NAME=zh_CN.UTF-8
LC_ADDRESS=zh_CN.UTF-8
LC_TELEPHONE=zh_CN.UTF-8
LC_MEASUREMENT=zh_CN.UTF-8
LC_IDENTIFICATION=zh_CN.UTF-8
LC_ALL=

经过分析错误为 LC_ALL 没设置值,LC_CTYPE 和 LC_MESSAGES 的值 zh_CN.UTF-8 系统未安装。

远程主机默认仅支持 en_US.UTF-8 ,而我的客户端默认配置为 zh_CN.UTF-8 ,结果远程时服务端继承了客户端配置,导致字符集报错。

此时无需在远程做任何配置,只需断开远程连接,将下面一行配置写入我的 Ubuntu 主机中的 ~./bashrc 即可:

export LC_ALL=C

原理

在设定主机 locale 时存在一个优先级,可总结为 LC_ALL > LC_* >LANG ,可以说 LC_ALL 是最上级设定或者强制设定,而 LANG 是默认设定值。

若需了解详情可拓展阅读参考文献。

参考文献

Perl 调试打印 HASH 内容

在调试 Perl 程序时常常需要打印哈希表内容,虽然可以直接使用 foreach 打印,但数据复杂了就难办了,此时可以将 Hash 表转换为 json 文本再打印:

use JSON;

my $data = {'info'=> "test", 'struct' => {'test1'=>'test1', 'test2'=>'test2'}};
my $json = new JSON;
#$json->sort_by(sub { ncmp($JSON::PP::a, $JSON::PP::b) });
my $json_text = $json->pretty->encode ($data);
print $json_text;

如果没有 json 包需要安装一下:

cpan -i JSON

如果下载太慢,可以使用 tuna 提供的 cpan 国内镜像源:

# 若tuna cpan不在镜像列表中则将其加入列表首位
if ! (
    perl -MCPAN -e 'CPAN::HandleConfig->load();' \
        -e 'CPAN::HandleConfig->prettyprint("urllist")' |
    grep -qF 'https://mirrors.tuna.tsinghua.edu.cn/CPAN/'
); then
    perl -MCPAN -e 'CPAN::HandleConfig->load();' \
        -e 'CPAN::HandleConfig->edit("urllist", "unshift", "https://mirrors.tuna.tsinghua.edu.cn/CPAN/");' \
        -e 'CPAN::HandleConfig->commit()'
fi

测试一下,效果还可以:

$ perl -e 'use JSON;
> 
> my $data = {'info'=> "test", 'struct' => {'test1'=>'test1', 'test2'=>'test2'}};
> my $json = new JSON;
> #$json->sort_by(sub { ncmp($JSON::PP::a, $JSON::PP::b) });
> my $json_text = $json->pretty->encode ($data);
> print $json_text;'
{
   "struct" : {
      "test2" : "test2",
      "test1" : "test1"
   },
   "info" : "test"
}

参考文献