golang中判断两个slice是否相等

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package main

import (
    "bytes"
    "fmt"
)

func main() {
    a := []byte{0, 1, 3, 2}
    b := []byte{0, 1, 3, 2}
    c := []byte{1, 1, 3, 2}

    fmt.Println(bytes.Equal(a, b))
    fmt.Println(bytes.Equal(a, c))
}

计算中文长度

1
2
title := "中国人"
fmt.Println(utf8.RuneCountInString(title)) // 输出 3

数据库初始化

什么是 DSN 信息?

DSN 全称为 Data Source Name,表示 数据源信息,用于定义如何连接数据库。不同数据库的 DSN 格式是不同的,这取决于数据库驱动的实现,下面是 go-sql-driver/sql 的 DSN 格式,如下所示:

1
2
// [用户名[:密码]@][协议(数据库服务器地址)]]/数据库名称?参数列表
[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...&paramN=valueN]

如何配置 sql. DB 的 SetMaxOpenConns SetMaxIdleConns 和 SetConnMaxLifetime

如何配置 sql. DB 的 SetMaxOpenConns SetMaxIdleConns 和 SetConnMaxLifetime

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import (
    "github.com/go-sql-driver/mysql"
    _ "github.com/go-sql-driver/mysql" // 引入 mysql 驱动
)

// 初始化单个数据库
func initDB() {
    var err error

    // 设置数据库连接信息
    config := mysql.Config{
        User:                    "root",
        Passwd:                  "123456",
        Net:                     "tcp",
        Addr:                    "127.0.0.1:3305",
        DBName:                  "goblog",
        AllowNativePasswords:    true,
    }

    // 准备数据库连接池
    db, err = sql.Open("mysql", config.FormatDSN())
    checkError(err)

    // 设置最大连接数,不能超过数据库本身设置的最大连接数 `show variables like 'max_connections';`
    db.SetMaxOpenConns(200) // yim 设置了 512
    // 设置最大空闲连接数
    db.SetMaxIdleConns(100) // yim 设置了 256
    // 设置每个连接的过期时间,这里的推荐,比较保守的做法是设置五分钟
    db.SetConnMaxLifetime(5 * time.Minute)

    err = db.Ping()
    checkError(err)
}

func checkError(err error) {
    if err != nil {
        log.Fatal(err)
    }
}

根据结构体返回字段名切片

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
func main()  {
    // 输出:[ `name`  `password`  `gender`  `create_time`  `update_time`  `id`  `number` ]
    fmt.Println(RawFieldNames(&User{}))
}

const dbTag = "db" // 根据哪个标签获取字段名称

type User struct {
    Name       string `db:"name"` // 用户名称
    Password   string `db:"password"` // 用户密码
    Gender     string `db:"gender"` // 男|女|未公开
    CreateTime time.Time `db:"create_time"`
    UpdateTime time.Time `db:"update_time"`
    Id         int64 `db:"id"`
    Number     string `db:"number"` // 学号
}

func RawFieldNames(in interface{}) []string {
    out := make([]string, 0)
    v := reflect.ValueOf(in)
    if v.Kind() == reflect.Ptr {
        v = v.Elem()
    }

    // we only accept structs
    if v.Kind() != reflect.Struct {
        panic(fmt.Errorf("ToMap only accepts structs; got %T", v))
    }

    typ := v.Type()
    for i := 0; i < v.NumField(); i++ {
        // gets us a StructField
        fi := typ.Field(i)
        if tagv := fi.Tag.Get(dbTag); tagv != "" {
            out = append(out, fmt.Sprintf(" `%s` ", tagv))
        } else {
            out = append(out, fmt.Sprintf( `"%s"` , fi.Name))
        }
    }

    return out
}

移除数组元素

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
func Remove(strings []string, strs ...string) []string {
    out := append([]string(nil), strings...)

    for _, str := range strs {
        var n int
        for _, v := range out {
            if v != str {
                out[n] = v
                n++
            }
        }
        out = out[:n]
    }

    return out
}

超时处理 & 定时器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// 超时处理
select {
    case <-ch:
    case <-time.After(time.Second * 1): // 1秒后超时触发
        fmt.Println("timeout")
}

// 定时器
count := 0
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
for {
    <-ticker.C
    fmt.Println(count)
    count++
    if count >= 5 {
        break
    }
}

获取真实IP

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
func GetRemoteAddr(r *http.Request) string {
    v := r.Header.Get("X-Forward-For")
    if len(v) > 0 {
        return v
    }
    return r.RemoteAddr
}

// 获取本机ip
var ip string
func GetLocalIP() string {
    if ip != "" {
        return ip
    }
    addrs, err := net.InterfaceAddrs()
    if err != nil {
        return ""
    }
    for _, address := range addrs {
        // check the address type and if it is not a loopback the display it
        if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
            if ipnet.IP.To4() != nil {
                ip = ipnet.IP.String()
                return ip
            }
        }
    }
    return ""
}

md5 && hmac

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
func Md5(str string) string {
    h := md5.New()
    h.Write([]byte(str))
    return hex.EncodeToString(h.Sum(nil))
}

func HmacSha256(data string, secret string) string {
    h := hmac.New(sha256.New, []byte(secret))
    h.Write([]byte(data))
    return hex.EncodeToString(h.Sum(nil))
}

// 简单的密码加密方案
func makePassword(pwd string) string {
    return helper.Md5(helper.HmacSha256("盐", pwd))
}

利用反射,结构体转map方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
// 结构体转map方法
func Struct2Map(obj interface{}) map[string]interface{} {
    t := reflect.TypeOf(obj)
    v := reflect.ValueOf(obj)

    var data = make(map[string]interface{})
    if t.Kind() != reflect.Struct {
        return data
    }

    for i := 0; i < t.NumField(); i++ {
        data[t.Field(i).Name] = v.Field(i).Interface()
    }

    return data
}

// 结构体转map方法,key直接取结构体上变量名的 json 名
func Struct2MapJson(obj interface{}) map[string]interface{} {
    t := reflect.TypeOf(obj)
    v := reflect.ValueOf(obj)

    var data = make(map[string]interface{})
    if t.Kind() != reflect.Struct {
        return data
    }

    for i := 0; i < t.NumField(); i++ {
        data[t.Field(i).Tag.Get("json")] = v.Field(i).Interface()
    }
    return data
}

// 结构体转map方法,处理嵌套结构体
func StructToMapByJson(obj interface{}) map[string]interface{} {
    t := reflect.TypeOf(obj)
    v := reflect.ValueOf(obj)

    var data = make(map[string]interface{})
    if t.Kind() != reflect.Struct {
        return data
    }

    for i := 0; i < t.NumField(); i++ {
        value := v.Field(i)

        // 处理嵌套结构体
        t2 := reflect.TypeOf(value.Interface())
        if t2.Kind() == reflect.Struct {
            for j := 0; j < t2.NumField(); j++ {
                temp := t2.Field(j)
                _ = temp
                data[t2.Field(j).Tag.Get("json")] = value.Field(j).Interface()
            }
            continue
        }

        data[t.Field(i).Tag.Get("json")] = value.Interface()
    }

    return data
}

同步队列+并发消费

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package main

import (
    "context"
    "fmt"
    "sync"
    "time"
)

// 同步队列+并发消费案例
type (
    Task struct {
        StartTime time.Time
    }

    TaskManager struct {
        Tasks chan *Task
        Wg    *sync.WaitGroup
        Ctx   context.Context
    }
)

func main() {
    timeoutCtx, cancel := context.WithTimeout(context.Background(), 10*time.Minute) // 10分钟超时
    defer cancel()

    var manager = &TaskManager{
        Tasks: make(chan *Task, 10), // 等待队列10,并发5 执行任务
        Wg:    &sync.WaitGroup{},
        Ctx:   timeoutCtx,
    }

    runDaemon(manager)

    // 生产者:负责生产任务
    var tasks []*Task
    tasks = append(tasks, &Task{})
    for i := 0; i < 20; i++ {
        manager.Wg.Add(1)
        manager.Tasks <- &Task{StartTime: time.Now()}
    }

    manager.Wg.Wait()
}

func runDaemon(manager *TaskManager) {
    // 并发 5 个协程消费队列
    for i := 0; i < 5; i++ {
        go func() {
            for {
                select {
                case task := <-manager.Tasks:
                    // todo 执行相关业务逻辑
                    fmt.Println("消费任务:start_time:", task.StartTime.String())
                    manager.Wg.Done()
                case <-manager.Ctx.Done():
                    return
                }
            }
        }()
    }
}

interface{}变量转为字符串值

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
// Strval 获取变量的字符串值
// 浮点型 3.0将会转换成字符串3, "3"
// 非数值或字符类型的变量将会被转换成JSON格式字符串
func Strval(value interface{}) string {
    // interface 转 string
    var key string
    if value == nil {
        return key
    }

    switch value.(type) {
    case float64:
        ft := value.(float64)
        key = strconv.FormatFloat(ft, 'f', -1, 64)
    case float32:
        ft := value.(float32)
        key = strconv.FormatFloat(float64(ft), 'f', -1, 64)
    case int:
        it := value.(int)
        key = strconv.Itoa(it)
    case uint:
        it := value.(uint)
        key = strconv.Itoa(int(it))
    case int8:
        it := value.(int8)
        key = strconv.Itoa(int(it))
    case uint8:
        it := value.(uint8)
        key = strconv.Itoa(int(it))
    case int16:
        it := value.(int16)
        key = strconv.Itoa(int(it))
    case uint16:
        it := value.(uint16)
        key = strconv.Itoa(int(it))
    case int32:
        it := value.(int32)
        key = strconv.Itoa(int(it))
    case uint32:
        it := value.(uint32)
        key = strconv.Itoa(int(it))
    case int64:
        it := value.(int64)
        key = strconv.FormatInt(it, 10)
    case uint64:
        it := value.(uint64)
        key = strconv.FormatUint(it, 10)
    case string:
        key = value.(string)
    case []byte:
        key = string(value.([]byte))
    default:
        newValue, _ := json.Marshal(value)
        key = string(newValue)
    }

    return key
}

map实现集合方案

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 第一种
m := make(map[string]bool)
m["a"] = true
if m["a"] {
    fmt.Println("a exist")
}
if m["b"] {
    fmt.Println("b exist")
} else {
    fmt.Println("b no exist")
}

// 第二种
m2 := make(map[string]struct{})
m2["c"] = struct{}{}
if _, ok := m2["c"]; ok {
    fmt.Println("c exist")
}
if _, ok := m2["d"]; ok {
    fmt.Println("d exist")
} else {
    fmt.Println("d no exist")
}

浮点数四舍五入

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Round 四舍五入
// 返回将 val 根据指定精度 precision(十进制小数点后数字的数目)进行四舍五入的结果,precision可为0
func Round(val float64, precision int) float64 {
    p := math.Pow10(precision)
    return math.Floor(val*p+0.5) / p
}

// 元转分
func Yuan2Fen(f float64) int64 {
    if f == 0 {
        return 0
    }
    decimalValue := decimal.NewFromFloat(f)
    decimalValue = decimalValue.Mul(decimal.NewFromInt(100))
    return decimalValue.BigInt().Int64()
}

// 分转元
func FenToYuan(i int64) string {
    if i == 0 {
        return "0.00"
    }
    decimalValue := decimal.NewFromInt(i)
    decimalValue = decimalValue.Div(decimal.NewFromInt(100))
    return decimalValue.String()
}

url编码特殊处理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// url.QueryEscape 不会编译“~”字符,但php那边会编译成"%7E",所以需要手动转换
func urlEncode(str string) string {
    specialChar := map[string]string{
        "~": "%7E",
    }
    for k, v := range specialChar {
        str = strings.Replace(url.QueryEscape(str), k, v, -1)
    }
    return str
}

下载网络文件到本地

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 下载文件到本地
// url 网络文件链接
// filepath 本地文件存放路径,如/Users/xxx/Downloads/test.wav
func DownLoad(url, filepath string) error {
    // Get the data
    resp, err := http.Get(url)
    if err != nil {
        return err
    }
    defer resp.Body.Close()
    // Create output file
    out, err := os.Create(filepath)
    if err != nil {
        return err
    }
    defer out.Close()
    // copy stream
    _, err = io.Copy(out, resp.Body)
    if err != nil {
        return err
    }
    return nil
}

获取链接后缀

1
2
3
4
5
func main() {
    url := "https://example.com/files/document.pdf"
    fmt.Println(path.Base(url)) // 输出 document.pdf
    fmt.Println(path.Ext(url))  // 输出 .pdf
}

用正则表达式处理html代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 去掉html代码中的标签,只保留纯文本
func TrimHtml(src string) string {
    //将HTML标签全转换成小写
    re, _ := regexp.Compile(`<[\S\s]+?>`)
    src = re.ReplaceAllStringFunc(src, strings.ToLower)
    //去除STYLE
    re, _ = regexp.Compile(`<style[\S\s]+?</style>`)
    src = re.ReplaceAllString(src, "")
    //去除SCRIPT
    re, _ = regexp.Compile(`<script[\S\s]+?</script>`)
    src = re.ReplaceAllString(src, "")
    //去除所有尖括号内的HTML代码
    re, _ = regexp.Compile(`<[\S\s]+?>`)
    src = re.ReplaceAllString(src, "")
    //去除连续的换行符
    re, _ = regexp.Compile(`\s{2,}`)
    src = re.ReplaceAllString(src, "\n\n")
    return strings.TrimSpace(src)
}

// 获取html代码中的图片链接
func GetHtmlImages(htmls string) []string {
    var imgRE = regexp.MustCompile(`<img[^>]+\bsrc=["']([^"']+)["']`)
    imgs := imgRE.FindAllStringSubmatch(htmls, -1)
    out := make([]string, len(imgs))
    for i := range out {
        out[i] = imgs[i][1]
    }
    return out
}