Go语言开发小技巧&易错点100例(四)

简介: Go语言开发小技巧&易错点100例(四)


本期看点(技巧类用【技】表示,易错点用【易】表示)

(1)goroutine控制并发数量的方式【技】

(2)Go发起HTTP请求【技】

(3)包循环依赖导致的异常【易】

正文如下:

1 goroutine控制并发数量的方式【技】

首先我们思考一个问题,为什么要控制goroutine的数量呢?

虽然goroutine的创建成本很低,而且占用的内存也很少,但是一旦数量没有控制,导致短时间内大量的goroutine同时执行也会造成内存崩溃、CPU占用率过高等问题,因此我们在生产级的项目中一定要注意控制好goroutine的数量,以免发生生产事故。

那么,我们都有哪些方式来控制goroutine的数量呢?

  • sync.WaitGroup
  • channel
  • sync.WaitGroup+channel
  • semaphore

(1)最简单的方式

func main() {
   group := sync.WaitGroup{}
   group.Add(3)
   for i := 0; i < 3; i++ {
      go func() {
         fmt.Println("hello...")
         group.Done()
      }()
   }
   group.Wait()
}

这种方式非常的简单,但是弊端就是不容易灵活扩展

(2)sync.WaitGroup+channel方式

type Pool struct {
   queue chan int
   wg    *sync.WaitGroup
}
func New(size int) *Pool {
   if size <= 0 {
      size = 1
   }
   return &Pool{
      queue: make(chan int, size),
      wg:    &sync.WaitGroup{},
   }
}
func (p *Pool) Add(delta int) {
   for i := 0; i < delta; i++ {
      p.queue <- 1
   }
   for i := 0; i > delta; i-- {
      <-p.queue
   }
   p.wg.Add(delta)
}
func (p *Pool) Done() {
   <-p.queue
   p.wg.Done()
}
func (p *Pool) Wait() {
   p.wg.Wait()
}

测试:

func main() {
   pool := pool.New(10)
   for i := 0; i < 100; i++ {
      pool.Add(1)
      go func() {
         time.Sleep(time.Second)
         fmt.Printf("%d hello...\n", i)
         pool.Done()
      }()
   }
   pool.Wait()
}

2 Go发起HTTP请求【技】

服务端:

type Student struct {
   Name string
   Age  int
}
func HttpServe() {
   /**
   URL:http://localhost:8080
   Method:Get
    */
   http.HandleFunc("/get", func(w http.ResponseWriter, r *http.Request) {
      str := r.URL.Query().Get("str")
      fmt.Println("Get Method Str is " + str)
      w.Write([]byte("Hello Http Get!"))
   })
   /**
   URL:http://localhost:8080
      Method:Get
      Param:str
    */
   http.HandleFunc("/get/form", func(w http.ResponseWriter, r *http.Request) {
      name := r.URL.Query().Get("name")
      age := r.URL.Query().Get("age")
      ageStr, err := strconv.Atoi(age)
      if err != nil {
         fmt.Println("err...")
      }
      stu := Student{Name: name, Age: ageStr}
      fmt.Println("Get Method Str is ", stu)
      w.Write([]byte("Hello Http Get Form!"))
   })
   /**
   URL:http://localhost:8080
      Method:Get
      Param:str
    */
   http.HandleFunc("/get/json", func(w http.ResponseWriter, r *http.Request) {
      fmt.Println("req method : ", r.Method)
      body, err := io.ReadAll(r.Body)
      if err != nil {
         fmt.Printf("获取请求体错误 , %v\n", err)
         return
      }
      fmt.Println("请求体 :", string(body))
      var stu Student
      if err = json.Unmarshal(body, &stu); err != nil {
         fmt.Printf("反序列化失败 , %v\n", err)
         return
      }
      fmt.Printf("反序列化成功,JSON解析结果 %+v", stu)
      w.Write([]byte("Hello Http Get Form!"))
   })
   err := http.ListenAndServe(":8080", nil)
   if err != nil {
      fmt.Println(err)
   }
}

Go发送Http请求:

func HttpGet() {
   resp, err := http.Get("http://localhost:8080/get?str=ymx") // url
   if err != nil {
      fmt.Printf("get请求失败 error: %+v", err)
      return
   }
   defer resp.Body.Close()
   body, err := io.ReadAll(resp.Body)
   if err != nil {
      fmt.Printf("读取Body失败 error: %+v", err)
      return
   }
   fmt.Println(string(body))
}
func HttpPost() {
   resp, err := http.PostForm("http://localhost:8080/form", 
      url.Values{
         "name": {"jack"}, 
      })
   if err != nil {
      fmt.Printf("postForm请求失败 error: %+v", err)
      return
   }
   defer resp.Body.Close()
   body, err := io.ReadAll(resp.Body)
   if err != nil {
      fmt.Printf("读取Body失败 error: %+v", err)
      return
   }
   fmt.Println(string(body))
}

3 包循环依赖导致的异常【易】

循环依赖是一个在代码层面很常见的概念了,简单来说就是A依赖B,B依赖A,从而导致的先有蛋还是先有鸡的问题,下面来一个示例:

package_a代码:

package package_a
import (
   "encoding/json"
   "other/article5/pack/package_b"
)
func MakeStudent(stu package_b.Student) string {
   bytes, _ := json.Marshal(stu)
   return string(bytes)
}

package_b代码:

package package_b
import "other/article5/pack/package_a"
type Student struct {
   Id   int64
   Name string
}
func (stu *Student) GetStuJSON() string {
   return package_a.MakeStudent(*stu)
}

测试方法:

package main
import (
   "fmt"
   "other/article5/pack/package_b"
)
func main() {
   student := package_b.Student{
      Name: "zs",
   }
   str:= student.GetStuJSON()
   fmt.Println(str)
}

执行结果:

如何避免循环依赖呢?

说实话没有什么特别好的办法,就是在平时写代码前先做好设计,设计好每一层的依赖关系,尽量不要产生额外的循环依赖即可。

相关文章
|
2天前
|
JavaScript Java Go
探索Go语言在微服务架构中的优势
在微服务架构的浪潮中,Go语言以其简洁、高效和并发处理能力脱颖而出。本文将深入探讨Go语言在构建微服务时的性能优势,包括其在内存管理、网络编程、并发模型以及工具链支持方面的特点。通过对比其他流行语言,我们将揭示Go语言如何成为微服务架构中的一股清流。
|
1天前
|
Ubuntu 编译器 Linux
go语言中SQLite3驱动安装
【11月更文挑战第2天】
16 7
|
1天前
|
关系型数据库 Go 网络安全
go语言中PostgreSQL驱动安装
【11月更文挑战第2天】
16 5
|
1天前
|
SQL 关系型数据库 MySQL
go语言数据库中mysql驱动安装
【11月更文挑战第2天】
13 4
|
1天前
|
存储 设计模式 安全
Go语言中的并发编程:从入门到精通###
本文深入探讨了Go语言中并发编程的核心概念与实践技巧,旨在帮助读者从理论到实战全面掌握Go的并发机制。不同于传统的技术文章摘要,本部分将通过一系列生动的案例和代码示例,直观展示Go语言如何优雅地处理并发任务,提升程序性能与响应速度。无论你是Go语言初学者还是有一定经验的开发者,都能在本文中找到实用的知识与灵感。 ###
|
1天前
|
安全 Go
用 Zap 轻松搞定 Go 语言中的结构化日志
在现代应用程序开发中,日志记录至关重要。Go 语言中有许多日志库,而 Zap 因其高性能和灵活性脱颖而出。本文详细介绍如何在 Go 项目中使用 Zap 进行结构化日志记录,并展示如何定制日志输出,满足生产环境需求。通过基础示例、SugaredLogger 的便捷使用以及自定义日志配置,帮助你在实际开发中高效管理日志。
9 1
|
2天前
|
关系型数据库 MySQL 数据库连接
go语言中打开数据库连接
【11月更文挑战第1天】
13 2
|
9天前
|
Go 数据安全/隐私保护 开发者
Go语言开发
【10月更文挑战第26天】Go语言开发
24 3
|
10天前
|
Java 程序员 Go
Go语言的开发
【10月更文挑战第25天】Go语言的开发
21 3
|
3月前
|
JSON 中间件 Go
go语言后端开发学习(四) —— 在go项目中使用Zap日志库
本文详细介绍了如何在Go项目中集成并配置Zap日志库。首先通过`go get -u go.uber.org/zap`命令安装Zap,接着展示了`Logger`与`Sugared Logger`两种日志记录器的基本用法。随后深入探讨了Zap的高级配置,包括如何将日志输出至文件、调整时间格式、记录调用者信息以及日志分割等。最后,文章演示了如何在gin框架中集成Zap,通过自定义中间件实现了日志记录和异常恢复功能。通过这些步骤,读者可以掌握Zap在实际项目中的应用与定制方法
125 1
go语言后端开发学习(四) —— 在go项目中使用Zap日志库
下一篇
无影云桌面