开发系列go-最佳实践

开发系列go-2

1.go 异常和错误(Error 接口)

在Go语言中,错误和异常是不相同的俩个概念,错误被认为是一种可以预期的结果;而异常则是一种非预期的结果, 发生异常可能表示程序中存在BUG或发生了其它不可控的问题。
Go语言推荐使 用 recover 函数将内部异常转为错误处理,这使得用户可以真正的关心业务相关的错误处理。

Recover()用法是:将Recover()写在defer中,并且在可能发生panic的地方之前,先调用此defer的东西(让系统方法域结束时,有代码要执行。)
当程序遇到panic的时候(当然,也可以正常的调用出现的异常情况),系统将跳过后面的代码,进入defer,如果defer函数中有recover(),则返回捕获到的panic的值。

func main() {
	fmt.Println(divide(3,0))
}

func divide(num1 ,num2 int )int {
	defer func() {
		if err := recover(); err !=nil {
			fmt.Printf("error: %s\n", err)
		}
	}()
	return num1/num2
}


//manager *serverManager表示该 method 属于 NewManager类型对象中的方法
func (manager *serverManager) check(c *gin.Context) {
	defer func() {
		if err := recover(); err != nil {
			logrus.Error("catch panic error:{}", err)
		}
	}()
	panic("我要抛出一个异常")

	c.JSON(200, gin.H{
		"message": "success",
	})
	return
}

Go语言没有异常系统,其使用 panic 触发宕机类似于其他语言的抛出异常,recover 的宕机恢复机制就对应其他语言中的 try/catch 机制。
在这里插入图片描述
2. 日志处理+日志切割

import (
	"edge-node-red/server"
	rotatelogs "github.com/lestrrat-go/file-rotatelogs"
	"github.com/sirupsen/logrus"
	"time"
)

func init() {
	path := "./app.log"
	logrus.SetLevel(logrus.DebugLevel)
	logrus.Info("==init==")
	logrus.SetFormatter(&logrus.JSONFormatter{
		TimestampFormat: "2006-01-02 15:04:05",
	})
	/* 日志轮转相关函数
	`WithLinkName` 为最新的日志建立软连接
	`WithRotationTime` 设置日志分割的时间,隔多久分割一次
	`WithMaxAge 和 WithRotationCount二者只能设置一个
	`WithMaxAge` 设置文件清理前的最长保存时间
	`WithRotationCount` 设置文件清理前最多保存的个数
	*/
	// 下面配置日志每隔 1 分钟轮转一个新文件,保留最近 3 天的日志文件,多余的自动清理掉。
	//	logfile, _ := os.OpenFile("./app.log", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0644)
	writer, _ := rotatelogs.New(
		path+".%Y%m%d%H%M",
		rotatelogs.WithLinkName(path),
		rotatelogs.WithMaxAge(time.Duration(72)*time.Hour),
		rotatelogs.WithRotationTime(time.Duration(24)*time.Hour),
	)
	logrus.SetOutput(writer)
}

3.判断为空:

func GetURL() string {
	url := config.Conf.NodeRed.Url
	logrus.Info("global variable:url={}", url)
	if len(url) == 0 || url == "" {
		url = "http://127.0.0.1:1880"
	}
	return url
}

4.channel
4-1.无缓冲通道

func main() {
    ch := make(chan int)    
    ch <- 10    
    fmt.Println("发送成功")
}
会报错:fatal error: all goroutines are asleep - deadlock!

func recv(c chan int) {
    ret := <-c    
    fmt.Println("接收成功", ret)
}

func main() {    
	ch := make(chan int)    
	go recv(ch) // 启用goroutine从通道接收值    
	ch <- 10    
	fmt.Println("发送成功")
}
运行正常。结论:无缓冲通道也被称为同步通道

4-2. 通过协程和channel实现协程同步(子协程执行后主线程再执行)

func main() {
	ch := make(chan struct{})
	count := 2 // count 表示活动的协程个数
	go func() {
		fmt.Println("Goroutine 1")
		ch <- struct{}{} // 协程结束,发出信号
	}()
	go func() {
		fmt.Println("Goroutine 2")
		ch <- struct{}{} // 协程结束,发出信号
	}()

	// 优雅的从通道循环取值
	for range ch {
		// 每次从ch中接收数据,表明一个活动的协程结束
		count--
		// 当所有活动的协程都结束时,关闭管道
		if count == 0 {
			close(ch)
		}
	}
	fmt.Println("--main--")
} 

4-3. 通过sync.WaitGroup实现协程同步(子协程执行后主线程再执行)

func main() {
	var wg sync.WaitGroup
	wg.Add(2) // 因为有两个动作,所以增加2个计数

	go func() {
		fmt.Println("Goroutine 1")
		wg.Done() // 操作完成,减少一个计数
	}()

	go func() {
		fmt.Println("Goroutine 2")
		wg.Done() // 操作完成,减少一个计数
	}()
	wg.Wait() // 等待,直到计数为0
	
	fmt.Println("--main--")
}

4-4. 多任务一个worker处理

package main
import (
	"fmt"
	"sync"
	"time"
)

var wg sync.WaitGroup

//工作线程
func workerPool(jobs <-chan int, results chan<- int) {
	for j := range jobs {
		fmt.Println("start job", j)
		time.Sleep(time.Second)
		fmt.Println("finish job", j)
		results <- j
	}
}

func init() {
	wg = sync.WaitGroup{}
}

func main() {
	const numJobs = 5
	jobs := make(chan int)    //
	results := make(chan int) //

	go workerPool(jobs, results)
	go func() {
		for r := range results {
			fmt.Println("results :", r)
			wg.Done() //接收到数据,表示完成了一份工作
		}
	}()

	for i := 1; i <= numJobs; i++ {
		wg.Add(1) //标记开始一份工作
		jobs <- i
	}
	wg.Wait()
}

4-5. 使用select 切换协程

func main() {
	ch1 := make(chan string)
	ch2 := make(chan string)

	go func() {
		for i := 0; i < 10; i++ {
			ch1 <- fmt.Sprintf("A%d", i)
		}
	}()

	go func() {
		for i := 0; i < 10; i++ {
			ch2 <- fmt.Sprintf("B%d", i)
		}
	}()

	go func() {
		for {
			select {
			case v := <-ch1:
				fmt.Println(v)
			case v := <-ch2:
				fmt.Println(v)
			}
		}
	}()
	time.Sleep(1e9)
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值