Linux 下默认会进行 5 次重发 SYN-ACK 包,总共耗时大概一分钟。由于
SYN-ACK 超时需要 63
秒,那么就给攻击者一个攻击服务器的机会,攻击者在短时间内发送大量的 SYN
包给服务端,用于耗尽服务端的 SYN 队列。为了应对 SYN 攻击的问题,linux
提供了几个 TCP
参数:tcp_syncookies、tcp_synack_retries、tcp_max_syn_backlog、tcp_abort_on_overflow
syn cookie
Linux 实现了一种称为 SYN cookie 的机制,通过
net.ipv4.tcp_syncookies 控制,设置为 1 表示开启。简单说
SYNcookie
就是将连接信息编码在ISN(initialsequencenumber)中返回给客户端,这时服务端不需要将半连接保存在队列中,而是利用客户端随后发送的
ACK 带回的 ISN 还原连接信息,以完成连接的建立,避免了半连接队列被攻击
SYN 包填满。
SYN 半连接队列的大小是由
/proc/sys/net/ipv4/tcp_max_syn_backlog
这个内核参数控制的,有些内核似乎也受 listen 的 backlog
参数影响,取的是两个值的最小值。当这个队列满了,不开启 syncookies
的时候,服务端会丢弃新来的 SYN 包,而客户端在多次重发 SYN
包得不到响应而返回(connection timeout)错误。但是,当服务端开启了
syncookies=1,那么 SYN 半连接队列就没有逻辑上的最大值了,并且
/proc/sys/net/ipv4/tcp_max_syn_backlog
设置的值也会被忽略。
我们看到 Format 里面具体处理逻辑在
AppendFormat 函数里面,再点开 AppendFormat
看看:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
switch std & stdMask { case stdYear: y := year if y < 0 { y = -y } b = appendInt(b, y%100, 2) case stdLongYear: b = appendInt(b, year, 4) case stdMonth: b = append(b, month.String()[:3]...) case stdLongMonth: m := month.String() b = append(b, m...) // ... 省略其他 case }
我们可以看到里面的 stdYear、stdLongYear
之类的常量实际上就是我们传入到 Format
的参数里面的数字。
stdNeedDate = 1 << 8// need month, day, year stdNeedClock = 2 << 8// need hour, minute, second stdArgShift = 16// extra argument in high bits, above low stdArgShift stdSeparatorShift = 28// extra argument in high 4 bits for fractional second separators stdMask = 1<<stdArgShift - 1// mask out argument )
cobra
的主要功能是创建强大的现代 cli 应用程序。目前市面上许多的著名的 Go
语言开源项目都是使用 Cobra 来构建的,例如:K8s、Hugo、etcd、Docker
等,是非常可靠的一个开源项目。
没有 cobra 之前用什么
如果不用 cobra,我们也可以使用 go 自带的 flag 标准库
flag 的基本用法
下面代码中,我们调用标准库 flag 的
StringVar 方法实现了对命令行参数 name
的解析和绑定,其各个形参的含义分别为命令行标识位的名称、默认值、帮助信息。
命令行参数支持如下三种命令行标志语法:
-flag 仅支持布尔类型
-flag x 仅支持非布尔类型
-flag=x 均支持
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
package main
import ( "flag" "fmt" )
funcmain() { var name string // 名称为 name、默认值为 "Go go go!" flag.StringVar(&name, "name", "Go go go!", "帮助信息") flag.StringVar(&name, "n", "Go go go!", "帮助信息") flag.Parse()
fmt.Printf("name: %s\n", name) }
执行:
1 2 3
// 均输出 abc go run main.go -name=abc go run main.go -n=abc
子命令实现
在我们日常使用的 CLI
应用中,另一个最常见的功能就是子命令的使用,一个工具它可能包含大量相关联的功能命令以此形成工具集,可以说是刚需,那么这个功能在标准库
flag 中可以如何实现呢,如下述示例:
➜ go run main.go go 2022/08/04 08:59:21 name: Go 语言 ➜ go run main.go php 2022/08/04 09:00:54 name: PHP 语言 ➜ go run main.go php -n abc 2022/08/04 09:01:05 name: abc