Golang DNS
2022-10-30
A 记录
package main
import (
"fmt"
"net"
)
func main() {
// 方法 1
// func ResolveIPAddr(network, address string) (*IPAddr, error)
ipAddr, err := net.ResolveIPAddr("ip", "www.google.com")
if err != nil {
fmt.Println("解析IP地址失败:", err)
} else {
fmt.Println("IP地址是:", ipAddr.IP)
}
// 方法 2
// func LookupMX(name string) ([]*MX, error)
// func LookupTXT(name string) ([]string, error)
// func LookupIP(host string) ([]IP, error)
// func LookupHost(host string) ([]string, error) // only IPv4
ips, err := net.LookupHost("www.google.com")
if err != nil {
fmt.Println("解析主机名失败:", err)
} else {
for _, ip := range ips {
fmt.Println("IP地址是:", ip)
}
}
}
MX 记录
package main
import (
"fmt"
"net"
)
func main() {
records, err := net.LookupMX("qq.com")
if err != nil {
fmt.Println("解析MX记录失败:", err)
} else {
fmt.Printf("%#v", records)
}
}
指定 DNS 服务器
func queryMX(dns string, domain string) ([]*net.MX, error) {
resolver := &net.Resolver{
PreferGo: true,
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
dialer := &net.Dialer{}
return dialer.DialContext(ctx, "udp", dns)
},
}
return resolver.LookupMX(context.Background(), domain)
}
完整版本
package main
import (
"context"
"fmt"
"net"
"strings"
"time"
)
var dnsServers = []string{
"223.5.5.5:53",
"114.114.114.114:53",
"8.8.8.8:53",
}
type IPType int
const (
NotIP IPType = iota
IPv4
IPv6
)
const (
DNSLookupTimeout = 500 * time.Millisecond // time.Duration
)
func GetIPType(host string) IPType {
ip := net.ParseIP(host)
if ip == nil {
return NotIP
}
if ip.To4() != nil {
return IPv4
}
if ip.To16() != nil {
return IPv6
}
return NotIP
}
type Resolver struct {
DNS string
Resolver *net.Resolver
}
func NewResolver(dns string) Resolver {
resolver := Resolver{}
resolver.DNS = dns
resolver.Resolver = &net.Resolver{
PreferGo: true,
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
dialer := &net.Dialer{}
return dialer.DialContext(ctx, "udp", dns)
},
}
return resolver
}
func (r Resolver) QueryA(domain string) ([]string, error) {
return r.Resolver.LookupHost(context.Background(), domain)
}
func (r Resolver) QueryMX(domain string) ([]*net.MX, error) {
ctx, cancel := context.WithTimeout(context.Background(), DNSLookupTimeout)
defer cancel()
return r.Resolver.LookupMX(ctx, domain)
}
func (r Resolver) Query(domain string) ([]*net.MX, error) {
addrs, err := r.QueryMX(domain)
if err != nil {
return nil, err
}
var result []*net.MX
for _, addr := range addrs {
// hostname := strings.TrimRight(addr.Host, ".")
ipType := GetIPType(addr.Host)
fmt.Printf("%s: %#v\n", addr.Host, ipType)
if ipType == NotIP {
ips, err := r.QueryA(addr.Host)
if err != nil {
continue
}
for _, ip := range ips {
result = append(result, &net.MX{Host: ip, Pref: addr.Pref})
}
}
}
return result, nil
}
var resolvers map[string]Resolver
func Query(domain string) ([]*net.MX, error) {
var addrs []*net.MX
var err error
for _, dns := range dnsServers {
var r Resolver
if r, ok := resolvers[dns]; !ok {
r = NewResolver(dns)
resolvers[dns] = r
}
addrs, err = r.Query(domain)
if err == nil {
break
}
fmt.Printf("Error: %s: %s: %#v\n", dns, domain, err)
}
return addrs, err
}
func QueryAsync(domain string, ch chan<- []*net.MX, errCh chan<- error) {
addrs, err := Query(domain)
if err != nil {
errCh <- err
return
}
ch <- addrs
}
func init() {
resolvers = make(map[string]Resolver)
}
func main() {
{
domains := []string{"qq.com", "gmail.com", "google.com"}
for _, domain := range domains {
fmt.Println(strings.Repeat("=", 100))
addrs, err := Query(domain)
if err != nil {
fmt.Printf("Error: %#v\n", err)
} else {
for _, addr := range addrs {
fmt.Printf("%#v\n", addr)
}
}
}
}
{
fmt.Println(strings.Repeat("=", 100))
ch := make(chan []*net.MX)
errCh := make(chan error)
go QueryAsync("google.com", ch, errCh)
select {
case addrs := <-ch:
fmt.Println("MX Records:")
for _, addr := range addrs {
fmt.Printf("%#v\n", addr)
}
case err := <-errCh:
fmt.Println("Error:", err)
}
}
}
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
。
Golang Viper
2022-07-22
package main
import (
"fmt"
"os"
"github.com/spf13/pflag"
"github.com/spf13/viper"
)
func main() {
// 1. 设置 Viper 配置
viper.SetConfigName("config") // 配置文件名(不带后缀)
viper.AddConfigPath(".") // 配置文件路径
viper.SetConfigType("yaml") // 配置文件类型
viper.AutomaticEnv() // 自动读取环境变量
// 2. 设置命令行参数
pflag.String("name", "", "project name")
pflag.String("host", "", "host address")
pflag.String("port", "", "port number")
pflag.String("config", "./config.yaml", "config file") // 配置文件参数
pflag.Parse()
viper.BindPFlags(pflag.CommandLine) // 将命令行参数绑定到 Viper
// 3. 读取配置文件
if configFile := viper.GetString("config"); configFile != "" {
fmt.Println(configFile)
if err := viper.ReadInConfig(); err != nil {
fmt.Fprintf(os.Stderr, "读取配置文件失败:%v\n", err)
os.Exit(1)
}
}
// 4. 读取配置项
projectName := viper.GetString("name")
port := viper.GetInt("port")
fmt.Printf("ProjectName: %s, Port: %d\n", projectName, port)
}
name: hello
host: 10.10.0.172
port: 9090
- 支持环境变量、命令行参数、配置文件。
- 支持多种配置文件,包括 JSON,YAML,TOML,INI 等。
- 支持监控配置文件的变化,会自动加载新的配置。
- 支持从远程加载配置,比如 etcd、zk、consul、redis 等,
也可以通过 RemoteProvider 接口自定义远程数据源:
type RemoteProvider interface {
Set(key string, value []byte) error
Watch(key string) (chan *RemoteResponse, chan error)
Get(key string) ([]byte, error)
}
Golang
2022-07-20
通过对 key 做哈希,来实现 Redis 服务器的分片。
用这个例子来演示这个小库的用法:
package main
import (
"crypto/rand"
"encoding/base64"
"fmt"
"log"
"time"
"github.com/go-redis/redis"
"github.com/serialx/hashring"
)
func RandomString(length int) (string, error) {
b := make([]byte, length)
_, err := rand.Read(b)
if err != nil {
return "", err
}
return base64.URLEncoding.EncodeToString(b)[:length], nil
}
func main() {
redisServers := map[string]int{
"redis1:6379": 200,
"redis2:6379": 100,
}
redisClients := []*redis.Client{}
for uri, _ := range redisServers {
redisClients = append(redisClients, redis.NewClient(&redis.Options{Addr: uri}))
}
log.Println(redisServers)
ring := hashring.NewWithWeights(redisServers)
stat := map[string]int{}
for uri, _ := range redisServers {
stat[uri] = 0
}
for i := 0; i < 100; i++ {
randstr, _ := RandomString(8)
key := "test:randkey:" + randstr
node, ok := ring.GetNode(key)
if !ok {
log.Panicf("cannot assign a redis client by key: %#v", key)
}
log.Printf("%s -> %s", key, node)
stat[node]++
var client *redis.Client
for _, _client := range redisClients {
if node == _client.Options().Addr {
client = _client
break
}
}
if client == nil {
log.Panicf("redis client assigned error: %#v", node)
}
client.Set(key, 1, time.Minute)
}
fmt.Println(stat)
}
Golang
2022-07-19
https://github.com/bits-and-blooms/bloom/v3
package main
import (
"fmt"
"github.com/bits-and-blooms/bloom/v3"
)
func main() {
filter := bloom.New(1000000, 5)
filter.Add([]byte("apple"))
filter.Add([]byte("banana"))
filter.Add([]byte("orange"))
fmt.Println(filter.Test([]byte("apple")))
fmt.Println(filter.Test([]byte("banana")))
fmt.Println(filter.Test([]byte("orange")))
fmt.Println(filter.Test([]byte("grape")))
fmt.Println(filter.Test([]byte("watermelon")))
}
Golang GUI
2022-06-20
明显,fyne 遥遥领先。
Golang
2022-06-10
type Weekday int
const (
Sunday Weekday = iota // 0
Monday // 1
Tuesday // 2
Wednesday // 3
Thursday // 4
Friday // 5
Saturday // 6
)
func (day Weekday) String() string {
names := [...]string{
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
}
if day < Sunday || day > Saturday {
return "Unknown"
}
return names[day]
}
type FileSizeUnit int
const (
_ = iota // 忽略第一个值 0,让后续常量从 1 开始递增
KB FileSizeUnit = 1 << (10 * iota) // 1 << (10*1) = 1 << 10 = 1024
MB // 1 << (10*2) = 1 << 20 = 1048576
GB // 1 << (10*3) = 1 << 30 = 1073741824
TB // 1 << (10*4) = 1 << 40 = 1099511627776
)
package main
import "fmt"
func main() {
{
const (
a = iota
b
c
)
fmt.Printf("%+v\n", []int{a, b, c})
// [0 1 2]
}
{
const (
a = 1
b = iota
c
)
fmt.Printf("%+v\n", []int{a, b, c})
// [1 1 2]
}
{
const (
a = 1
b = 2
c = iota
)
fmt.Printf("%+v\n", []int{a, b, c})
// [1 2 2]
}
{
const (
a = 1
b = iota
c = iota
)
fmt.Printf("%+v\n", []int{a, b, c})
// [1 1 2]
}
}
总结
- 首次出现是这个常量块中变量的顺序(0 开始)
- 后面的常量如果没有定义,就表示是按照前面的常量定义来,只是 iota 代表的值 + 1 了
- 如果想跳过一个值,可以使用下划线