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)
}
复制代码

执行结果:

网络异常,图片无法展示
|


如何避免循环依赖呢?

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


相关文章
|
3天前
|
存储 JSON 监控
Viper,一个Go语言配置管理神器!
Viper 是一个功能强大的 Go 语言配置管理库,支持从多种来源读取配置,包括文件、环境变量、远程配置中心等。本文详细介绍了 Viper 的核心特性和使用方法,包括从本地 YAML 文件和 Consul 远程配置中心读取配置的示例。Viper 的多来源配置、动态配置和轻松集成特性使其成为管理复杂应用配置的理想选择。
16 2
|
4天前
|
监控 Go API
Go语言在微服务架构中的应用实践
在微服务架构的浪潮中,Go语言以其简洁、高效和并发处理能力脱颖而出,成为构建微服务的理想选择。本文将探讨Go语言在微服务架构中的应用实践,包括Go语言的特性如何适应微服务架构的需求,以及在实际开发中如何利用Go语言的特性来提高服务的性能和可维护性。我们将通过一个具体的案例分析,展示Go语言在微服务开发中的优势,并讨论在实际应用中可能遇到的挑战和解决方案。
|
1天前
|
Go
go语言中的 跳转语句
【11月更文挑战第4天】
9 4
|
1天前
|
JSON 安全 Go
Go语言中使用JWT鉴权、Token刷新完整示例,拿去直接用!
本文介绍了如何在 Go 语言中使用 Gin 框架实现 JWT 用户认证和安全保护。JWT(JSON Web Token)是一种轻量、高效的认证与授权解决方案,特别适合微服务架构。文章详细讲解了 JWT 的基本概念、结构以及如何在 Gin 中生成、解析和刷新 JWT。通过示例代码,展示了如何在实际项目中应用 JWT,确保用户身份验证和数据安全。完整代码可在 GitHub 仓库中查看。
11 1
|
1天前
|
Go 索引
go语言中的循环语句
【11月更文挑战第4天】
8 2
|
1天前
|
Go C++
go语言中的条件语句
【11月更文挑战第4天】
10 2
|
3天前
|
Go 调度 开发者
探索Go语言中的并发模式:goroutine与channel
在本文中,我们将深入探讨Go语言中的核心并发特性——goroutine和channel。不同于传统的并发模型,Go语言的并发机制以其简洁性和高效性著称。本文将通过实际代码示例,展示如何利用goroutine实现轻量级的并发执行,以及如何通过channel安全地在goroutine之间传递数据。摘要部分将概述这些概念,并提示读者本文将提供哪些具体的技术洞见。
|
6月前
|
网络协议 Linux Go
分享一个go开发的工具-SNMP Server
分享一个go开发的工具-SNMP Server
145 0
|
6月前
|
搜索推荐 Linux Go
分享一个go开发的端口转发工具-port-forward
分享一个go开发的端口转发工具-port-forward
101 0
|
缓存 IDE 数据可视化
Go 日常开发常备第三方库和工具(中)
重点和大家分享下我们日常开发中所使用到的一些第三方库与工具。 这里我主要将这些库分为两类: 业务开发 基础工具开发