Go Interface & Duck Typing

Go 不需要像 Java 那样显式地使用 implement 说明某一数据类型实现了 interface,只要某一数据类型实现了 interface 所定义的方法名签,那么就称该数据类型实现了 interface。interface 的语言特性可以容易地做到接口定义和具体实现解耦分离,并将注意力转移到如何使用 interface ,而不是方法的具体实现,我们也称这种程序设计为 Duck Typing。文本将描述 Go 是如何通过 interface 来实现 Duck Typing。 本文提供的源代码都是基于 go1.7rc6 版本。 1. Duck Typing 了解实现原理之前,我们可以简单过一下 Go 的 Duck Typing 示例。 package main type Ducker interface { Quack() } type Duck struct {} func (_ Duck) Quack() { println("Quaaaaaack!") } type Person struct {} func (_ Person) Quack() { println("Aha?!") } func inTheForest(d Ducker) { d.Quack() } func main() { inTheForest(Duck{}) inTheForest(Person{}) } // result: // Quaaaaaack!...

June 5, 2017

让你的 shell 脚本变得可控

刚开始接触 shell 脚本的时候,最痛苦的地方在于出了问题,却不容易定位问题。 shell 脚本遇到错误,“大部分” 情况下都会继续执行剩下的命令,最后返回 Zero Exit Code 并不代表着结果正确。 这让人很难发现问题,它不像其他脚本语言,遇到 语法错误 和 typo 等错误时便会立即退出。 如果想要写出容易维护、容易 debug 的 shell 脚本,我们就需要让 shell 脚本变得可控。 set -e 默认情况下,shell 脚本遇到错误并不会立即退出,它还是会继续执行剩下的命令。 [[email protected] ~]# cat example #!/usr/bin/env bash # set -e sayhi # this command is not available. echo "sayhi" [[email protected] ~]# ./example ./example: line 4: sayhi: command not found sayhi 我们知道 Linux/Unix 用户等于系统的时候,内核会加载 .bashrc 或者 .bash_profile 里的配置。 不同 shell 版本会使用不同的 rc/profile 文件,比如 zsh 版本的 rc 文件名是 ....

March 20, 2017

shebang - #!

写脚本的时候通常会在脚本的开头加上 shebang, 系统会将这段内容作为解释器指令,比如 bash shell 脚本。 $> cat example #!/usr/bin/bash echo "HaHa" $> chmod +x ./example $> ./example HaHa 只要为脚本添加了可执行的属性,那么内核在执行脚本的时候,会调用 shebang 描述的解释器来执行脚本。 ./exmaple 其实等价于 /usr/bin/bash ./example。shebang 描述的解释器需要写其绝对路径或者相对路径,因为内核并不会在用户设置的 PATH 里找解释器。关于 shebang,讨论最多的应该是 兼容性 和 版本控制 问题。 兼容性 Linux 和 Unix 在存放解释器的具体路径不太一致,比如 Linux 会放到 /usr/bin/ 中,而 openBSD 会放到 /usr/local/bin/ 中。不同包管理器在安装解释器的时候,存放的位置也不尽相同。 当你在 Mac 上写了 shell 脚本,测试并提交到代码库。 结果等到部署的那一天,执行脚本的时候发现找不到解释器了。 为了解决这个问题,可以通过 env 来解决,因为它在 Linux 和 Unix 存放的位置相同。 $> cat exmaple #!/usr/bin/env bash echo "HaHa" env 会在用户设置的 PATH 中查找解释器第一次出现的具体路径。 虽然办法比较 tricky,但是这种方式能解决脚本解释器的兼容性问题。...

March 19, 2017

Netfilter 初探

Linux 内核在 2.4.x 版本中正式引入 Netfilter 模块,该模块负责网络数据包过滤和 Network Address Translation。 Netfilter 代表着一系列的 Hook ,被内核嵌入到 TCP/IP 协议栈中,数据包在穿梭协议栈时,Hook 会检查数据包,从而达到访问控制的作用。 规则链 Netfilter 模块默认定义了五种类型的 Hook: PREROUTING INPUT FORWARD OUTPUT POSTROUTING 在 Netfilter 里,Hook 也称为 Chain,规则链 我们可以从数据包的来源和走向入手来进行分析这条五条规则链的设计。 首先,数据包按照来源可以分成 Incoming 和 Outgoing 这两种类型。 Incoming 数据包是指其他网卡发来的数据包。这类数据包可能直接奔向用户态的程序, 也有可能被内核转发到其他机器或者其他网卡上,这需要内核做路由判定。 而 Outgoing 数据包是用户态程序准备要发送的数据包。 数据包到达内核之后,内核会为它选择合适的网卡和端口,在此之后便会一层层地穿过协议栈,内核在此过程之中会做出路由判定。 一般情况下,客户端所使用的高端口号。在 Linux 下,我们可以通过 cat /proc/sys/net/ipv4/ip_local_port_range 查看系统会随机使用的端口号范围。 需要注意的是,如果这是内网和外网之间的通信,内核会使用到 NAT 技术来对地址进行转化。 对于 Incoming 数据包而言,内核路由前需要对数据包进行 Destination NAT 转化。 同理,数据包在路由之后也需要做 Source NAT 转化。 根据上面的分析,可以得到以下结论: Incoming 数据包的目的地就在本地:PREROUTING -> INPUT Incoming 数据包需要转发:PREROUTING -> FORWARD -> POSTROUTING Outgoing 数据包:OUTPUT -> POSTROUTING 不同走向的数据包都必定会通过以上五个环节中的部分环节,只要系统管理员在五个环节中设置关卡,就可以做到系统的访问控制。...

March 17, 2017