由于 golang 使用时间长了之后 你所面临的需求开始变的复杂 功能会变得越来越多, 所以在这个过程中 积累了一些 第三方库这个记录一下
这一片文章 可能会有点长, 耐心看吧, 少年.

安装包

发送邮件

url: github.com/go-gomail/gomail

用途: 服务器发送邮件给你的邮箱

简单的封装:

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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package hCommon

import (
"custom/happy/hLog"
"github.com/go-gomail/gomail"
"strings"
)

type EmailParam struct {
// ServerHost 邮箱服务器地址,如腾讯企业邮箱为smtp.exmail.qq.com
ServerHost string
// ServerPort 邮箱服务器端口,如腾讯企业邮箱为465
ServerPort int
// FromEmail 发件人邮箱地址
FromEmail string
// FromPasswd 发件人邮箱密码(注意,这里是明文形式)
FromPasswd string
// Toers 接收者邮件,如有多个,则以英文逗号(“,”)隔开,不能为空
Toers string
// CCers 抄送者邮件,如有多个,则以英文逗号(“,”)隔开,可以为空
CCers string
}

type AutoEmail struct {
ep *EmailParam
m *gomail.Message
}

var gAutoEmail *AutoEmail

func NewAutoEmail(ep *EmailParam) *AutoEmail {

if gAutoEmail == nil {
hLog.Info("Init Email")
gAutoEmail = &AutoEmail{
ep: ep,
m: gomail.NewMessage(),
}
gAutoEmail.Init()
}
return gAutoEmail
}

func SendEmail(subject, body string) {
if gAutoEmail == nil {
return
}
gAutoEmail.SendEmail(subject, body)
}

func (this *AutoEmail) Init() {
if len(this.ep.Toers) == 0 {
return
}
toers := []string{}

for _, tmp := range strings.Split(this.ep.Toers, ",") {
toers = append(toers, strings.TrimSpace(tmp))
}

this.m.SetHeader("To", toers...)
toers = []string{}

if len(this.ep.CCers) != 0 {
for _, tmp := range strings.Split(this.ep.CCers, ",") {
toers = append(toers, strings.TrimSpace(tmp))
}
this.m.SetHeader("Cc", toers...)
}

this.m.SetAddressHeader("From", this.ep.FromEmail, "小魏")
}

func (this *AutoEmail) SendEmail(subject, body string) {
this.m.SetHeader("Subject", subject)
this.m.SetBody("text/html", body)

d := gomail.NewDialer(this.ep.ServerHost, this.ep.ServerPort, this.ep.FromEmail, this.ep.FromPasswd)

err := d.DialAndSend(this.m)
if err != nil {
hLog.Error("邮件发送失败 \n subject ===> ", subject, "\n body ===> ", body, "\n [Error] ===> ", err)
}
}

用法:

1
2
3
4
5
6
7
hCommon.NewAutoEmail(/*初始化参数*/);

hCommon.SendEmail("邮件标题", "你要发送的邮件内容")

// body 的两种写法
hCommon.SendEmail("邮件标题", fmt.Sprintf(`IP %s <br>
Role: %s`, "123.123.123.123", "master"))

获取 linux cpu 和 使用内存

url: github.com/struCoder/pidusage

用途: 标题说的很清楚, 主要用于项目的负载均衡算法中一个(根据 cpu 和 内存 计算出一个负载 比较低额服务器地址)

这个不用封装, 安装即可使用,

现在只能用于 linux, window 暂时不支持

用法:

1
2
3
systemInfo, err := pidusage.GetStat(os.Getpid())

fmt.Println(systemInfo.CPU, systemInfo.Memory)

生成 唯一ID

url: github.com/bwmarrin/snowflake

用途: 比如生成 房间ID ,

为啥不用时间戳, 如果在高并发的情况下, 可能一毫秒 有好几个请求, 这个时间戳 就可能不准确

简单封装:

1
2
3
4
5
6
7
8
9

// work id 是在集群中 可能有好几个 服务节点 运行同一个服务, 每个节点都有他自己的 id, 将他自己的id 传进俩, 比如房间服务器,
func GenId(workId int64) int64 {
node, err := snowflake.NewNode(workId)
if err != nil {
return -1
}
return node.Generate().Int64()
}

用法:

1
GenId(1);

服务器节点间的数据传输协议 protobuf

url: github.com/protobuf/proto
url: github.com/protobuf/protoc-gen-go

用法: 这里就不介绍了, 上一篇文章有介绍.

在内存中实现 排行榜功能

url: github.com/XanthusL/zset

用途: 实现如世界排行榜
起因: 如果人员太多, 导致服务器的 排行 查询效率, 低下, 这里你可以 只排前 1000 或者是 前 10000 名用户.

用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 根据 用户等级 进行排名

this.zSet = zset.New()

lv := 10
key := "vker" // 唯一标识
this.zSet.Set(float64(lv), key, "用户数据") // 储存在内存中

// 排序 前 100
this.zSet.RevRange(0, 99, func( _ float64, _ int64, item interface{}) {

})

// 找到 最后一名
this.zSet.RevRange(this.zSet.Length()-1, this.zSet.Length()-1, func( _ float64, _ int64, item interface{}) {

})

// 删除
this.zSet.Delete(key)

负载均衡算法

url: github.com/lafikl/liblb

用法: 这个用法暂时 先不写, 如果你想了解, 你可以去看 它这个包里面的 测试用用例, 以及里面的源码, 就基本上可以知道怎么使用了.

主要是这个演示有点麻烦.

日志输出

url: github.com/op/go-logging
url: github.com/natefinch/lumberjack

用途: 服务器日志 输出, 提供了时间, 第二个包提供了 将日志 使用时间划分,

简单封装:

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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
package hLog

import (
"github.com/natefinch/lumberjack"
"github.com/op/go-logging"
"os"
)

/*
type Password string

func (p Password) Redacted() interface{} {
return logging.Redact(string(p))
}
*/

// 0 - 5 C-D

var log = logging.MustGetLogger("happyyLog")

var format = logging.MustStringFormatter(
`%{color}%{time:15:04:05.000} %{shortfile} %{longfunc} > %{level:.4s} %{id:03x}%{color:reset} %{message}`,
)

func InitLogger(fileName string, fileSize int, fileMax int, logLv int, LogConsolePrint bool) {

backendFile := logging.NewLogBackend(&lumberjack.Logger{
Filename: "./logs/" + fileName + ".log",
MaxSize: fileSize, // megabytes
Compress: true, // disabled by default

MaxAge: 7,
MaxBackups: fileMax,
LocalTime: true,
}, "", 0)

backendFileErr := logging.NewLogBackend(&lumberjack.Logger{
Filename: "./logs/" + fileName + "-error.log",
MaxSize: fileSize, // megabytes
Compress: false, // disabled by default

MaxAge: 7,
MaxBackups: fileMax,
LocalTime: true,
}, "", 0)

backendFileFormatter := logging.NewBackendFormatter(backendFile, format)
backendFileErrFormatter := logging.NewBackendFormatter(backendFileErr, format)

backendFileLeveled := logging.AddModuleLevel(backendFileFormatter)
backendFileLeveled.SetLevel(logging.Level(logLv), "")
backendFileErrLeveled := logging.AddModuleLevel(backendFileErrFormatter)
backendFileErrLeveled.SetLevel(logging.ERROR, "")

var backend2 *logging.LogBackend = nil
var backend2Formatter logging.Backend

if LogConsolePrint {
backend2 = logging.NewLogBackend(os.Stdout, "", 0)
backend2Formatter = logging.NewBackendFormatter(backend2, format)
}

if backend2 == nil {
logging.SetBackend(backendFileLeveled, backendFileErrLeveled)
} else {
logging.SetBackend(backendFileLeveled, backendFileErrLeveled, backend2Formatter)
}
}

var Debug = log.Debug
var Debugf = log.Debugf
var Info = log.Info
var Infof = log.Infof
var Notice = log.Notice
var Noticef = log.Noticef
var Warn = log.Warning
var Warnf = log.Warningf
var Error = log.Error
var Errorf = log.Errorf

var Critical = log.Error
var Criticalf = log.Criticalf

var Fatal = log.Fatal
var Fatalf = log.Fatalf
var Panic = log.Panic
var Panicf = log.Panicf

用法:

1
hLog.InitLogger("Default", 10, 7, 4, true) // 这个配置 可以存储 7天服务器日志输出, 每个日志文件 10M, 使用控制台输出. 创建一个 Default 的文件, 过滤 Info 以上的 信息. 这个4 其实是个枚举,

定时任务

url: github.com/robfig/cron

用途: 比如服务器要 在每天十二点 实行一个事情等这个样的东西, 或者在服务器启动后 每 1个小时 采集一次数据等.

用法:

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
63
64
65
66
67
68
69
70
71
72
// 比如实现一个 过多少秒后 执行一次 任务 然后移除的例子, 这个是截取 服务器中 微信小游戏订阅的例子.
// 相信聪明的你 一定是可以看得懂的

type TimerTaskJob struct {
ProjName string
Platform string
Openid string
Appid string
AppSecret string
TemplateId string

ID *cron.EntryID
Cron *cron.Cron
}

func (this *TimerTaskJob) Run() {
d := &common.WechatSubscribeRetMap{}
sTime := time.Now()
err := common.SendSubscribeWechat(this.Appid, this.AppSecret, this.ProjName, this.Platform, &sendSubscribeWechatDataS{
Touser: this.Openid,
TemplateId: this.TemplateId,
Page: "",
Data: sendSubscribeWechatInnerDataS{
Phrase1: snedSubscribeWechatInnerValueS{
Value: "任务完成",
},
Date2: snedSubscribeWechatInnerValueS{
Value: sTime.Format("2006年01月02日"),
},
Time3: snedSubscribeWechatInnerValueS{
Value: sTime.Format("15:04"),
},
Thing4: snedSubscribeWechatInnerValueS{
Value: "定时任务完成了",
},
},
}, d)
if err != nil {
hLog.Errorf("订阅消息消息发送失败 err ===> %s", err.Error())
}

if d.Errcode != 0 {
hLog.Errorf("订阅消息消息发送失败 errCode ===> %d, err ===> %s", d.Errcode, d.ErrMsg)
} else {
hLog.Info("订阅消息发送成功")
}

// 执行一次 直接从调度中移除
this.Cron.Remove(*this.ID)
}

func (this *TimerTaskJob) SetData(id *cron.EntryID, cron *cron.Cron) {
this.ID = id
this.Cron = cron
}

func main() {
timerTaskJob := &TimerTaskJob{
ProjName: data.ProjName,
Platform: data.ProjName,
Openid: data.Sign,
Appid: gameConfig.AppId,
AppSecret: gameConfig.AppSecret,
TemplateId: data.TemplateId,
}
id, err := this.cronSecond.AddJob(fmt.Sprintf("*/%d * * * * *", data.Time), timerTaskJob)
if err != nil {
hLog.Errorf("添加定时任务失败 err ===> %s", err.Error())

}
timerTaskJob.SetData(&id, this.cronSecond)
}

http

url: github.com/gin-gonic/gin

用途: 既然是 服务器 就得支持 http, webSocket, tcp, udp, 这几个常用的连接方式
使用这个库, 只是可以让你 实现一个 http,请求 用更少的代码.

用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
func main() {
router := gin.New()
router.Use(gin.Recovery())
router.Use(hCommon.Cors()) // 跨域设置
router.GET("/", home)
}

func home(ctx *gin.Context) {
r := ctx.Request
w := ctx.Writer
hLog.Debug("Url ==> home")
if r.URL.Path != "/" {
http.Error(w, "Api not found", http.StatusNotFound)
return
}
if r.Method != "GET" {
http.Error(w, "Post type is not GET", http.StatusMethodNotAllowed)
}

_, err := w.WriteString("happy server framework")
if err != nil {
hLog.Error("No Status Gate {home} WriteString fail, ERROR => ", err)
}
}

注意: 使用 gin时候在每个绑定的函数里面 不要使用 在开启一个协程 然后在协程 响应用户, 不然会一直报一个 你写的数据 大于 Content-Length

WebSocket

url: github.com/gorilla/websocket

用法:

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
// 这里我 配合 gin 和 webscoket 一块

var upgrader = websocket.Upgrader{
//HandshakeTimeout: 0,
//ReadBufferSize: 0,
//WriteBufferSize: 0,
//WriteBufferPool: nil,
//Subprotocols: nil,
//Error: nil,
CheckOrigin: func(r *http.Request) bool {
return true
},
//EnableCompression: false,
}


gin.SetMode(gin.ReleaseMode)
router := gin.New()
router.Use(gin.Recovery())
router.GET("/", home)
router.GET("[表情]", func(ctx *gin.Context) {
conn, err := upgrader.Upgrade(ctx.Writer, ctx.Request, nil)
if err != nil {
_, _ = ctx.Writer.WriteString("Server internal error")
return
}

_, pkg, err := conn.ReadMessage()
}

// 这里 只是只是 一个简单的 不能直接使用, 当然也可以使用, 需要自己 进行二次修改, 调整成自己想要的样子.

今天就先暂时补充这个多, 后面再继续添加. 时间不够用了, 哈哈哈 .

最后更新: 2019年11月20日 16:03

原始链接: https://leng521.top/posts/792bb197/