Golang
2022-10-01
package main
import (
"bytes"
"encoding/gob"
"fmt"
)
type Person struct {
Name string
Age int
}
func main() {
// 编码
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
err := enc.Encode(Person{"John", 30})
if err != nil {
panic(err)
}
// 解码
dec := gob.NewDecoder(&buf)
var p Person
err = dec.Decode(&p)
if err != nil {
panic(err)
}
fmt.Println(p)
}
Golang
2022-09-16
# tree -L 1 -d .
.
├── api
├── doc
├── lib
├── misc
├── src
└── test
6 directories
# tree -L 1 -d src/
src/
├── archive
├── bufio
├── builtin
├── bytes
├── cmd
├── compress
├── container
├── context
├── crypto
├── database
├── debug
├── embed
├── encoding
├── errors
├── expvar
├── flag
├── fmt
├── go
├── hash
├── html
├── image
├── index
├── internal
├── io
├── log
├── math
├── mime
├── net
├── os
├── path
├── plugin
├── reflect
├── regexp
├── runtime
├── sort
├── strconv
├── strings
├── sync
├── syscall
├── testdata
├── testing
├── text
├── time
├── unicode
├── unsafe
└── vendor
46 directories
tree -L 2 -d src/net/
src/net/
├── http
│ ├── cgi
│ ├── cookiejar
│ ├── fcgi
│ ├── httptest
│ ├── httptrace
│ ├── httputil
│ ├── internal
│ ├── pprof
│ └── testdata
├── internal
│ └── socktest
├── mail
├── rpc
│ └── jsonrpc
├── smtp
├── testdata
├── textproto
└── url
19 directories
Golang
2022-09-13
awesome-go 上面列出来的项目:
- jakehl/goid
- Generate and Parse RFC4122 compliant V4 UUIDs.
- twharmon/gouid
- Generate cryptographically secure random string IDs with just one allocation.
- aidarkhanov/nanoid
- A tiny and efficient Go unique string ID generator.
- muyo/sno
- Compact, sortable and fast unique IDs with embedded metadata.
- oklog/ulid
- Go implementation of ULID (Universally Unique Lexicographically Sortable Identifier).
- uniq - No hassle safe, fast unique identifiers with commands.
- agext/uuid
- Generate, encode, and decode UUIDs v1 with fast or cryptographic-quality random node identifier.
- gofrs/uuid
- Implementation of Universally Unique Identifier (UUID). Supports both creation and parsing of UUIDs. Actively maintained fork of satori uuid.
- google/uuid
- Go package for UUIDs based on RFC 4122 and DCE 1.1: Authentication and Security Services.
- edwingeng/wuid
- An extremely fast globally unique number generator.
- rs/xid
- Xid is a globally unique id generator library, ready to be safely used directly in your server code.
我自己又在 GitHub 上搜罗了几个:
package main
import (
"os/exec"
guuid "github.com/google/uuid"
suuid "github.com/satori/go.uuid"
)
func UseGoogle() string {
id := guuid.New()
return id.String()
}
func UseSatori() string {
id := suuid.NewV4()
return id.String()
}
func UseUuidgen() string {
id, _ := exec.Command("uuidgen").Output()
return string(id)
}
func main() {
fmt.Println("Google UUID:", UseGoogle())
fmt.Println("Satori UUID:", UseSatori())
fmt.Println("Uuidgen UUID:", UseUuidgen())
}
Golang
2022-09-13
得知了 Go 谚语这么个东西,搜索一番,找到:
- https://go-proverbs.github.io/
- https://github.com/jboursiquot/go-proverbs
- https://www.youtube.com/watch?v=PAAkCSZUG1c
原来是类似 Python 之禅(import this
)一样的东西。
- Don't communicate by sharing memory, share memory by communicating. 别通过共享内存通信,要通过通信共享内存。
- Concurrency is not parallelism. 并发不是并行。
- Channels orchestrate; mutexes serialize.
- The bigger the interface, the weaker the abstraction. 接口越大, 抽象越弱。
- Make the zero value useful.
interface{}
says nothing.
- Gofmt's style is no one's favorite, yet gofmt is everyone's favorite. 别争论风格问题,不管喜不喜欢,用 gofmt 就是了。
- A little copying is better than a little dependency. 少量的复制好过引入新的依赖。
- Syscall must always be guarded with build tags.
- Cgo must always be guarded with build tags.
- Cgo is not Go. 别用 cgo。
- With the unsafe package there are no guarantees. 慎用 unsafe 包(安全无保障)
- Clear is better than clever. 清晰的代码比聪明的代码好。
- Reflection is never clear. 慎用反射(代码不清晰)。
- Errors are values.
- Don't just check errors, handle them gracefully.
- Design the architecture, name the components, document the details.
- Documentation is for users. 文档是给用户的(注意代码本身的可读性)。
- Don't panic. 不要滥用
panic
。
Protobuf
2022-09-12
enum Gender {
UNKNOWN = 0;
MALE = 1;
FEMALE = 2;
}
转换成 Golang 之后是这个样子:
type Person_Gender int32
const (
Person_UNKNOWN Person_Gender = 0
Person_MALE Person_Gender = 1
Person_FEMALE Person_Gender = 2
)
// Enum value maps for Person_Gender.
var (
Person_Gender_name = map[int32]string{
0: "UNKNOWN",
1: "MALE",
2: "FEMALE",
}
Person_Gender_value = map[string]int32{
"UNKNOWN": 0,
"MALE": 1,
"FEMALE": 2,
}
)
func (x Person_Gender) Enum() *Person_Gender {
p := new(Person_Gender)
*p = x
return p
}
func (x Person_Gender) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (Person_Gender) Descriptor() protoreflect.EnumDescriptor {
return file_example_proto_enumTypes[0].Descriptor()
}
func (Person_Gender) Type() protoreflect.EnumType {
return &file_example_proto_enumTypes[0]
}
func (x Person_Gender) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use Person_Gender.Descriptor instead.
func (Person_Gender) EnumDescriptor() ([]byte, []int) {
return file_example_proto_rawDescGZIP(), []int{0, 0}
}
Protobuf
2022-09-12
相较于文本编码的 JSON
- 速度快(序列化和反序列化)
- 数据小(二进制数据流更加紧凑)
- 数据类型丰富(enum,map...)
- 可读性差
数据类型
Protobuf 类型 |
描述 |
Go 对应类型 |
int32 |
有符号 32 位整数,采用可变长度编码,适合较小的整数 |
int32 |
int64 |
有符号 64 位整数,采用可变长度编码 |
int64 |
uint32 |
无符号 32 位整数,采用可变长度编码 |
uint32 |
uint64 |
无符号 64 位整数,采用可变长度编码 |
uint64 |
sint32 |
有符号 32 位整数,采用 ZigZag 编码,适合负数较多的情况 |
int32 |
sint64 |
有符号 64 位整数,采用 ZigZag 编码 |
int64 |
fixed32 |
无符号 32 位整数,固定 4 字节编码,对于大整数比 uint32 更高效 |
uint32 |
fixed64 |
无符号 64 位整数,固定 8 字节编码 |
uint64 |
sfixed32 |
有符号 32 位整数,固定 4 字节编码 |
int32 |
sfixed64 |
有符号 64 位整数,固定 8 字节编码 |
int64 |
float |
单精度 32 位浮点数 |
float32 |
double |
双精度 64 位浮点数 |
float64 |
bool |
布尔值,取值为 true 或 false |
bool |
string |
包含 UTF - 8 编码或 7 位 ASCII 文本的字符串,长度不能超过 2^32 |
string |
bytes |
任意的字节序列,长度不能超过 2^32 |
[]byte |
message |
自定义的数据结构,由多个字段组成,可以嵌套定义 |
struct |
enum |
用于定义一组命名的常量值 |
Go 枚举实现 |
repeated |
在字段前加上 repeated 关键字表示该字段是一个数组 |
Go 数组 |
map<KeyType, ValueType> |
从 proto3 开始支持的映射类型,用于表示键值对 |
map |
-
枚举类型,有另一篇文章可供参考:Protobuf 枚举类型的实现
-
数组(Repeated)示例:
message Person {
repeated string hobbies = 5;
}
-
映射(Map)示例:
message Person {
map<string, string> contacts = 6;
}
这里 contacts
是一个键为字符串、值也为字符串的映射。
基本流程
- 定义数据结构(.proto 文件)
- 使用编译器生成目标编程语言代码
-
在程序中引入生成的代码
-
Protobuf 编译器: protoc,作用是生成目标语言的数据定义代码
生成的代码
需要到 GitHub 下载。
-
Protobuf 运行时,作用是序列化和反序列化的基础库
Lang |
Repo |
C++ |
https://github.com/protocolbuffers/protobuf/blob/main/src |
Java |
https://github.com/protocolbuffers/protobuf/blob/main/java |
Python |
https://github.com/protocolbuffers/protobuf/blob/main/python |
Golang |
https://github.com/protocolbuffers/protobuf-go |
示例
-
proto 文件
syntax = "proto3";
package example;
message Person {
enum Gender {
UNKNOWN = 0;
MALE = 1;
FEMALE = 2;
}
string name = 1;
int32 age = 2;
Gender gender = 3;
}
-
编译:
$ protoc --go_out=. --go_opt=Mexample.proto=./generated example.proto
$ find .
.
./example.proto
./generated
./generated/example.pb.go
-
调用 proto 生成的 Go 代码
package main
import (
"fmt"
"log"
pb "example/generated" // 导入生成的包
"google.golang.org/protobuf/proto"
)
func main() {
// 创建一个 Person 实例
person := &pb.Person{
Name: "Alice",
Age: 30,
Gender: pb.Person_FEMALE,
}
// 序列化
data, err := proto.Marshal(person)
if err != nil {
log.Fatalf("Failed to marshal: %v", err)
}
// 反序列化
newPerson := &pb.Person{}
err = proto.Unmarshal(data, newPerson)
if err != nil {
log.Fatalf("Failed to unmarshal: %v", err)
}
fmt.Printf("Deserialized person: %+v\n", newPerson)
}
$ go mod init example
$ go mod tidy
$ go build -o main.exe .
$ ./main.exe
Deserialized person: name:"Alice" age:30 gender:FEMALE
HTTPS TLS
2022-09-04
HSTS 全称 HTTP Strict Transport Security,中文就是 HTTP 严格传输安全。
HSTS 的作用是通过特定的 HTTP 响应头告知浏览器,未来访问该网站时必须使用 HTTPS 连接,确保通信的安全性。这可以避免用户通过 HTTP 访问时的自动重定向,从而减少延迟。
对于同时支持 HTTP 和 HTTPS 的站点,HSTS 强制客户端始终使用 HTTPS,防止潜在的中间人攻击。主流浏览器都已全面支持 HSTS。
HSTS 于 2012 年成为互联网标准建议(RFC 6797),并逐渐被广泛采用。
背景
SSL 剥离攻击(SSLStrip),HTTP 降级攻击
受益于一整套相对可靠的公钥基础设施(PKI),直接的 HTTPS 通信是无法中间人攻击的。
中间人的伪造证书会被校验出来。
但是部分网站都是配置 HTTP 和 HTTPS 地址共存。
工作原理
通过 Strict-Transport-Security
响应头(下面称之为 HSTS 头),告诉客户端应该采用 HTTPS 连接。
客户端以后再访问这个网站就会自动重定向到 HTTPS(即使指定了 HTTP 协议)。
语法
Strict-Transport-Security: max-age=<expire-time>
Strict-Transport-Security: max-age=<expire-time>; includeSubDomains
Strict-Transport-Security: max-age=<expire-time>; includeSubDomains; preload
max-age
作用时长(秒),表示在这么长的时间之内都应该直接请求 HTTPS 协议。
includeSubDomains
对所有子域名生效
preload
添加到 HSTS 预加载列表需要的
例如:
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
表示两年之内,当前域名以及所有子域名,都通过 HTTPS 访问。
预加载列表
要求原文:
- Serve a valid certificate.
- Redirect from HTTP to HTTPS on the same host, if you are listening on port 80.
- Serve all subdomains over HTTPS.
- In particular, you must support HTTPS for the www subdomain if a DNS record for that subdomain exists.
- Serve an HSTS header on the base domain for HTTPS requests:
- The max-age must be at least 31536000 seconds (1 year).
- The includeSubDomains directive must be specified.
- The preload directive must be specified.
- If you are serving an additional redirect from your HTTPS site, that redirect must still have the HSTS header (rather than the page it redirects to).
问题
- 第一次访问不受 HSTS 保护
- 如果网站想回退到 HTTP,可能会受阻
Nginx HSTS 配置
-
80 rewrite 到 https
server {
listen 80;
server_name example.com;
# rewrite ^(.*)$ https://$host$1 permanent;
return 301 https://$server_name$request_uri;
}
-
HSTS 头
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
PKP:Public Key Pinning
和 HSTS 类似,服务器告诉浏览器网站的证书指纹,浏览器缓存起来。
后面的每次访问都会计算证书指纹,如果和之前缓存的哈希不匹配,则向用户发出警告,存在中间人攻击风险。
计算证书指纹的方法:
- SPKI
- 哈希,对整个证书计算哈希值,更加安全
和 HSTS 类似的是工作方式,目的(提升安全性),不同的是手段,HSTS 是为了确保采用安全的协议,PKP 是确保证书不被伪造。
参考资料与拓展阅读
USB 计算机硬件
2022-08-30
在人类的历史长河中,很少有一种技术或者传输标准能像USB那样跟我们的生活息息相关,甚至到了没有不行的地步。
USB对于今天的人们来说,就好像是空气,是水,是我们每天必需但是又熟视无睹的东西,没有多少人知道它从哪来,也没多少人关心它要往哪去,对于大多数人来说,它平凡得不能再平凡了。
但是,在我们“电子攻城狮”的眼里,它太有趣了,它是目前使用率最高的接口,它是我们身边林林总总电子设备之间的高速公路。
因此我们必须关注它,如果有必要,我们还不得不去了解如何才能实现它。作为一个USB开发者(电子爱好者),接下来我会为大家揭开USB神秘的面纱,带大家去了解USB是怎么出现并且逐渐演化的,以及在它出现后给我们带来了什么。
Linux 操作系统
2022-08-28
发行版
- Minix
- Unix 阵营
- 闭源
- 开源
- FreeBSD
- NetBSD
- OpenBSD
- DragonFly BSD
- Linux 阵营
- Android (AOSP)
- Debian
- RedHat
- OpenSUSE
- Gentoo
- Arch Linux
Debian / Ubuntu 和 RHEL / CentOS 衍生版本太多,就不列了。
我们常见的 Linux 发行版应该差不多都能找到自己的位置。
桌面环境
图形库主要就是两种:GTK,Qt
- 基于 GTK
- GNOME
- Xfce
- Cinnamon
- MATE
- LXDE
- Unity:ubuntu
- Budgie
- 基于 Qt
- KDE Plasma
- LXQt
- Deepin Desktop Environment (DDE)
- UKUI:ubunty kylin
个人看法
- 在国际关系越来越趋于对抗的这个局面下,Linux 对于中俄等国都有战略安全的重要作用。
- 国产 Linux 系统厂商大有搞头。
- 关于国产系统:
- 国产 Linux 桌面只有 Deepin 可以一战,而且做了太多工作,应该得到尊敬。
- 国产服务器系统我一个都没有用过,不知道如何。
- Unix 系统的生态和 Linux 完全不是同一个级别。
- 除非有特别的原因,否则应该直接选择 Linux。
- Deepin 等国产 Linux 系统提供商想搞什么国产根系统,想切断和社区的关联,在我看来是非常愚蠢的。
- 反而应该加大对社区的投入。
- 如果不知道该选哪个 Linux 发行版,我的建议是 Ubuntu。
- 无论是服务器还是个人使用,都可以直接选择上一个版本 Ubuntu LTS
- 如果比较喜欢尝鲜,个人使用可以选最新版本 Ubuntu
- 如果对 Ubuntu snap 不满意,就选择 Debian。
- 如果问选哪个桌面环境,就选 GNOME
- KDE 我没有用过,不知道
- 选 GNOME 作为默认桌面的多,应该是有道理的
- 桌面并没有那么重要,方便快捷的命令行操作才是 Linux 精髓
- 如果对发行版不满,就在基准系统上通过增删组件达成自己的目的,不要觉得换一个系统就能完美解决问题。
- 如果有时间折腾不同的发行版,还不如去玩一会儿游戏。
HTTP
2022-08-20
Server Push
服务器主动推送资源,客户端请求 A 的时候,服务器把 A 相关的资源 B, C, D 都一起推送给客户端。
-
Early Hints
HTTP 103 状态码。客户端请求 A 的时候,
- 如果是普通情况,服务器返回 200 状态码,带上资源信息
- 如果应用上 Early Hints,服务器返回 103 状态码,带上需要资源信息(Link 头),然后是资源信息
好处就是节省了浏览器解析 HTML 获取子资源信息的延迟。刚解析 103 头,就可以开始请求子资源了。
就最近的几个月,主流浏览器开始提供支持。
-
Preload Critical Assets
就是 HTML link 头中的 rel="preload"
,提前加载文件,避免按照文件解析生成的调用链顺序来加载,而提升资源加载速度。
谷歌博客显示,在 Chrome 106 和其他基于 Chromium 的浏览器的下个版本中,默认情况下将禁用对 HTTP/2 服务器推送(HTTP/2 Server Push)的支持。
HTTP/2 允许服务器在实际请求之前 “推送” 服务端可能需要的资源, HTTP/2 的 Server Push 特性解决了 HTTP/1.x 的无脑按顺序加载资源的问题,本意是提高网页的响应性能。
然而这功能逻辑本身就有问题,比如资源存放在单个业务服务器上,并行推送多个静态资源只会降低响应速度,性能不升反降。而对于前后端分离的业务来说,HTTP/2 本身就支持多路复用,server push 只能稍微降低浏览器解析 html 的时间,对现代浏览器来说性能提升可以忽略不计。
HTTP/2 时代也只有 1.25% 的 HTTP/2 站点使用了这个特性。在 HTTP/3 出来之后,该功能更是彻底被遗忘了,最新的分析中,网站对 HTTP/2 的支持率从 1.25% 下降到 0.7%。
替代方案
103 Early Hints 是 Server Push 的首选替代方案,它具有 Push 的许多优点,而缺点则少得多。与服务器推送资源不同,103 Early Hints 只向浏览器发送可能受益于请求的资源提示。浏览器可以控制它是否需要这些资源,比如浏览器已经在 HTTP 缓存中拥有这些资源,则无需额外加载。
预加载关键资源是另一种选择,它允许页面和浏览器一起工作,在页面加载的早期抢先加载关键资源。它不如 Server Push 或 Early Hints 快 —— 但它不会延迟关键页面资源,而另外两种解决方案都可能发生这种情况。
参考资料与拓展阅读