golang – net.Dial() 獲取 whois資料

本文是 net.Dail() 的一個實例。用於獲取域名的whois資料。

已知要獲取域名 python.com.tw 的whois資料,需要訪問 whois.twnic.net.tw 的 43 端口。(通過插件 github.com/zonedb/zonedb 可以獲取whois server信息)

先看程序:

domain := "python.com.tw"

const DefaultReadLimit = 1 << 10 << 10 // 讀取 1M

conn, err := net.Dial("tcp", "whois.cnnic.cn:43") // 建立連結
if err != nil {
	fmt.Println("client dial err =", err)
}

defer conn.Close() // 執行完畢後,關閉連結

// 發送請求內容
if _, err := conn.Write([]byte(domain + "\r\n")); err != nil {
	fmt.Println("client dial write err =", err)
}

// 讀取內容
body, _ := io.ReadAll(io.LimitReader(conn, DefaultReadLimit))
fmt.Println(string(body))

conn, err := net.Dial("tcp", "whois.cnnic.cn:43") 建立和 whois server 之間的連結。

再發送要請求的內容 _, err := conn.Write([]byte(domain + "\r\n"))

最後 創建一個 reader 最大讀取 1M io.LimitReader(conn, DefaultReadLimit),然後再通過 io.ReadAll() 讀取全部內容

Golang viper – 管理設定檔的工具

大型項目中,通常把一些設定放入單獨的設定文檔中。viper 是管理這些設定文檔的工具。

安裝 viper go get github.com/spf13/viper

特點:

  • 設定預設值
  • 讀取 JSON、TOML、YAML、HCL、envfile 和 Java 屬性設定檔
  • 即時觀看和重新讀取設定檔(可選)
  • 從環境變數中讀取
  • 從遠端配置系統(etcd 或 Consul)讀取並觀察更改
  • 從命令列標誌讀取
  • 從緩衝區讀取
  • 設定明確的值

例子:

func main() {
	//viper.SetConfigName("app") // name of config file (without extension)
	//viper.SetConfigType("yaml")
	viper.SetConfigFile(".env")
	viper.AddConfigPath(".")
	err := viper.ReadInConfig() // Find and read the config file
	if err != nil {             // Handle errors reading the config file
		panic(fmt.Errorf("fatal error config file: %w", err))
	}

	fmt.Println(viper.Get("version"))
	fmt.Println(viper.Get("animal"))
}

官方文檔: https://github.com/spf13/viper

golang – log 日誌包

log是 Go 標準庫提供的,不需要另外安裝。可直接使用。它提供了3組函數:

  • Print/Printf/Println:正常輸出日誌;
  • Panic/Panicf/Panicln:輸出日誌後,以拼裝好的字符串為參數調用panic
  • Fatal/Fatalf/Fatalln:輸出日誌後,調用os.Exit(1)退出程序。
import "log"

func main() {
	Name := "howard"
	Age := 11

	log.Printf("%s login, age:%d", Name, Age)
	log.Panicf("Oh, system error when %s login", Name)
	log.Fatalf("Danger! hacker %s login", Name)
}

可在每條輸出的文本前增加一些額外信息,如日期時間、文件名等。

// src/log/lgo.go
const (
	Ldate         = 1 << iota     // the date in the local time zone: 2009/01/23
	Ltime                         // the time in the local time zone: 01:23:23
	Lmicroseconds                 // microsecond resolution: 01:23:23.123123.  assumes Ltime.
	Llongfile                     // full file name and line number: /a/b/c/d.go:23
	Lshortfile                    // final file name element and line number: d.go:23. overrides Llongfile
	LUTC                          // if Ldate or Ltime is set, use UTC rather than the local time zone
	Lmsgprefix                    // move the "prefix" from the beginning of the line to before the message
	LstdFlags     = Ldate | Ltime // initial values for the standard logger
)
  • Ldate:輸出當地時區的日期,如2021/02/07
  • Ltime:輸出當地時區的時間,如12:45:45
  • Lmicroseconds:輸出的時間精確到微秒,設置了該選項就不用設置Ltime了。如12:45:45.123123
  • Llongfile:輸出長文件名+行號,含包名
  • Lshortfile:輸出短文件名+行號,不含包名,如main.go:20
  • LUTC:如果設置了Ldate或Ltime,將輸出 UTC 時間,而非當地時區
func main() {
	Name := "howard"
	Age := 11

	log.SetFlags(log.Lshortfile | log.Ldate | log.Lmicroseconds)

	log.Printf("%s login, age:%d", Name, Age)
  // 輸出: 2023/09/11 16:29:26.833582 log1.go:11: howard login, age:11
}

標準庫log並沒有日誌等級的設定。 如果有用到日誌等級,可以參考 zap 。以及日誌切割 包lumberjack

golang – GORM

本文章簡單介紹Gorm 的操作:

安裝需要的包:

go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql

gorm.io/driver/mysql 是針對 github.com/go-sql-driver/mysql 進行封裝,是之適用 gorm.io/gorm 接口。

引用包:

import (
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

模型, 是標準的struct,下面包含設置

type UserInfo struct {
	ID       int
	User     string `gorm:"column:user"`
	Password string `gorm:"column:password"`
}

func (UserInfo) TableName() string {
	return "user"
}

以下是列的一些約定:

type Foo struct {
  ID        uint      // 列名是 `id`
  Name      string    // 列名是 `name`
  Birthday  time.Time // 列名是 `birthday`
  CreatedAt time.Time // 列名是 `created_at`
}

默認情況下 ID 是主鍵,可以通過 gorm:"primaryKey" 指定其他的,例如:

UUID   string `gorm:"primaryKey"`

連結Mysql 以及查詢

  db, _ := gorm.Open(mysql.Open("root:xxxx@tcp(127.0.0.1:3306)/test_db?charset=utf8"))

	var user UserInfo
	//db.Where("id = ?", 1).First(&user)

	db.First(&user, "id = ?", 2)
	fmt.Println(user)

	var users []UserInfo
	db.Where("id > ?", 0).Find(&users)
	fmt.Println(users)

更新&刪除

user.User = "xxx"
db.Save(&user)

db.Delete(&user)

Gorm是功能很全面的ORM,全功能ORM也會損耗一些效率。

參考:https://gorm.io/zh_CN/docs/

golang – sqlx

這篇文章介紹 sqlx 。在項目中我們通常可能會使用database/sql連接MySQL資料庫。sqlx可以認為是Go語言內置database/sql的超集。增加了 scan 內容到 struct,並增加了一些 Get(),Select() 等函數。

安裝:

go get -u github.com/jmoiron/sqlx

同樣需要安裝 github.com/go-sql-driver/mysql,並加載:

import (
	_ "github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
)

連結資料庫:

db, _ := sqlx.Open("mysql", "root:xxx@tcp(127.0.0.1:3306)/test_db?charset=utf8")

查詢:

type User struct {
	Id       int    `db:"id"` // 必須大寫Id, 格式必須是 `db:"id"` 要有 : 要有 ""
	User     string `db:"user"`
	Password string `db:"password"`
}

func main() {
  ...

  var u2 User
	db.Get(&u2, "select * from user where id = ?", 1)
	fmt.Println(u2)
	u3 := []User{}
	db.Select(&u3, "select * from user limit 2")
	fmt.Println(u3)

  ...
}

插入&更新&刪除:

sqlStr := "insert into user(name, password) values (?,?)"
ret, err := db.Exec(sqlStr, "沙河小王子","xxxxxx")

sqlStr := "update user set name=? where id = ?"
ret, err := db.Exec(sqlStr, 39, "xxxxx")

sqlStr := "delete from user where id = ?"
ret, err := db.Exec(sqlStr, 6)

參考: http://jmoiron.github.io/sqlx/

golang 操作 mysql

本篇文章介紹:database/sql。 它是golang 內建的,只提供一個sql操作的統一接口。不過要連結mysql需要安裝 mysql 的實現。

安裝需要的包:

go get -u github.com/go-sql-driver/mysql

程序中必須加載2個包:

import (
	"database/sql"
	_ "github.com/go-sql-driver/mysql"
)

database/sql 包 是golang官方提供的一個接口。 github.com/go-sql-driver/mysql 包 mysql 的對接口的實現。

_ "github.com/go-sql-driver/mysql" 表示只加载 “github.com/go-sql-driver/mysql” 包的 init。然后统一有 标准的 database/sql调用。

連結Mysql:

db, err := sql.Open("mysql", "root:xxx@/test_db?charset=utf8mb4")
if err != nil {
    panic(err)
}

defer db.Close()

defer db.Close() 当程序结束时,执行关闭mysql连接。

查詢&處理數據:

rows, err := db.Query("SELECT id, area FROM zip limit 3")
defer rows.Close()

for rows.Next() {
    var id int
    var area string

    if err := rows.Scan(&id, &area); err != nil {
        panic(err)
    }

    fmt.Printf("%d \t %s\n", id, area)
}

必须通过 rows.Scan() 读取到对应变量

Gin、 Iris 簡單比較

Golang Gin和Iris 都是很優秀的Web框架。 優缺點比較如下:

  • 性能方面 。Gin在處理高併發請求時,性能更優越。
  • 易用性方面 。Gin比Iris更簡潔、更易於使用。
  • 功能方面 。Iris支援更多的應用場景,例如Web服務、RESTful API、Websocket、RPC等。
  • 可擴展性方面 。Iris具有可擴展性和高性能,適合處理大型企業級應用和高負載的Web應用。

選擇適合的工具取決於具體需求,不同的需求場景,存在不同的選擇。對於簡單的RESTful API或輕量級Web應用,可以選擇Gin;對於大型企業級應用和高負載的Web應用,可以選擇Iris。

官方提供的文檔方面:

Gin文檔 有多種語言,中文、日文、英文等,但功能全部已案例的方式說明。

Iris文檔 目前只有英文(對英文不好的同學、不友好)。功能說明很詳細。

Go 冒泡排序

冒泡排序算法是一種比較排序算法,它通過不斷地比較相邻元素的大小,將相對大的元素向右移動,相對小的元素向左移動,就像氣泡在水中一樣。

以下是在Golang中實現冒泡排序的代碼:

package main

import "fmt"

func bubbleSort(arr []int) {
    n := len(arr)
    for i := 0; i < n-1; i++ {
        for j := 0; j < n-i-1; j++ {
            if arr[j] > arr[j+1] {
                arr[j], arr[j+1] = arr[j+1], arr[j]
            }
        }
    }
}

func main() {
    arr := []int{9, 8, 7, 6, 5, 4, 3, 2, 1}
    bubbleSort(arr)
    fmt.Println(arr)
}

在上述代碼中,bubbleSort函數接收一個整數切片作為參數。函數使用兩重循環來對整數切片進行排序。外層循環控制比較的次數,內層循環實現每一步的比較和交換。在內層循環中,我們比較相邻的元素,如果前一個元素大於後一個元素,就將它們交換位置。通過多輪的比較和交換,最終得到一個有序的整數切片。