Go语言——测试与性能 上

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 Tair(兼容Redis),内存型 2GB
简介: Go语言——测试与性能 上

测试与性能

作为一名合格的开发者,不应该在程序开发完之后才开始写测试代码。使用 Go 语言的测试 框架,可以在开发的过程中就进行单元测试和基准测试。和 go build 命令类似,go test 命 令可以用来执行写好的测试代码,需要做的就是遵守一些规则来写测试。而且,可以将测试无缝 地集成到代码工程和持续集成系统里。

1. 测试

在计算机编程中,单元测试(Unit Testing )又称为模块测试,是针对程序模块(软件 设计的最小单位 )来进行正确性检验的测试工作。程序单元是应用的最小可测试部件,在 过程化编程中,一个单元就是单个程序,包括函数、过程等;对于面向对象编程, 最小单元就是方法,包括 基类(超类)、抽象类或者派生类 (子类)中的方法。

单元测试是用来测试包或者程序的一部分代码或者一组代码的函数。测试的目的是确认目标 代码在给定的场景下,有没有按照期望工作。

测试的目的自然是确认代码是否正常工作,例如测试代码是否可以成功地向数据库中 插入一条记录,这种测试叫作“正向路径”测试,就是在正常执行的情况下,保证代码不产生错误的测试

另外一种情况是测试代码是否会产生预期的错误,例如程序对数据库进行查询时没有找到任何结果,或者对数据库做了无效的更新,那么应该返回一个可以控制的错误,而不是导致程序崩渍,这种测试即为“负向路径”的测试场景,保证代码不仅会产生错误,而 且是预期的错误。

总之,不管如何调用或者执行代码,所写的代码行为都是可预期的测试才算通过。

在 Go 语言里有几种方法写单元测试:

基础测试(basic test)只使用一组参数和结果来测试 一段代码。

表组测试(table test)也会测试一段代码,但是会使用多组参数和结果进行测试。

也可以使用 些方法来模仿( mock )测试代码 要使用到的外部资源,如数据库或 者网络服务器 例如当外部资源不可用的时候,模拟这些资源的行为可以使测试正常进行。

最后, 在构建自己的网络服务时,有几种方法可以在不运行服务的情况下,调用服务的功能进行测试。

1.1 单元测试

testing是Go语言的一个Package,它提供自动化测试功能,通过 go test 命令能够自动执行如下形式的函数:

func TestXxx( *testing .T)

其中 Xxx 可以是任何字母、数字、字符串,但是 Xxx 的第一个字母不能是小写字母(即对外可访问)。 在这些函数中,使用 Error、Fail或相关方法来返回失败信号。

要编写 个新的测试模块,需要创建 个名称以 _test.go 结尾的文件,该文件包含 TestXxx 函数,最后将该文件放在与被测试的包相同的包目录中 。该文件将被排除在正常的程序包之外,但在运行go test命令时将被调用。运行go help test 或go help testflag可以了解更详细的信息。

1. 第一个测试函数

./02_testing/test01/test01.go

要测试的代码如下,下面的Age函数中,如果输入的参数值小于0则返回0,如果输入大于0则返回相应数值:

package main
func Age(n int) int {
  if n > 0 {
    return n
  }
  n = 0
  return n
}

测试代码如下所示,现在测试的是如果输入小于0的数字,程序是否会返回相应的数字:

./02_testing/test01/test01_test.go

package main
import "testing"
func TestAge(t *testing.T) {
  var (
    input   = -100
    exected = 0
  )
  actual:=Age(input)
  if actual!=exected {
    t.Errorf("Age(%d) = %d, 预期为 %d",input,actual,exected)
  }
}

执行测试命令:

go test ./02_testing

$ go test ./02_testing/test01
ok      go.standard.library.study/02_testing    (cached)

这时候如果我们把Age函数修改了,当输入的参数小于0的时候,应该返回-1,测试函数不动,我们可以看到:

$ go test ./02_testing/test01
--- FAIL: TestAge (0.00s)
    test01_test.go:12: Age(-100) = -1, 预期为 0
FAIL
FAIL    go.standard.library.study/02_testing/test01     0.022s
FAIL

这就是基础测试,下面来看表组测试,可以提供多组数据的测试方式。

2. 表组测试

测试讲究覆盖率,按照上面的方法,当要覆盖更多情况的时候,显然通过修改代码的方式很笨拙。这时候可以采用表组测试的方法写测试代码,标准库中有很多测试是使用这种方式写的。

例如,以下程序的作用是判断一个数字是否为素数:

./02_testing/test02/test02.go

package main
// 大于1的自然数中,除了1和它本身以外不再有其他因数的数称为质数
func isPrime(value int) bool {
  if value <= 3 {
    return value >= 2
  }
  if value%2 == 0 || value%3 == 0 {
    return false
  }
  for i := 5; i*i < value; i += 6 {
    if value%i==0||value%(i+2)==0 {
      return false
    }
  }
  return true
}

./02_testing/test02/test02_test.go

package main
import "testing"
func TestIsPrime(t *testing.T) {
  var primeTests = []struct {
    input    int  // 输入
    expected bool // 期望结果
  }{
    {1, false},
    {2, true},
    {3, true},
    {4, false},
    {5, true},
    {6, false},
    {7, false}, // 这个是错误用例
  }
  for _, tt := range primeTests {
    actual := IsPrime(tt.input)
    if actual != tt.expected {
      t.Errorf("IsPrime(%d)=%v,预期为 %v",tt.input,actual,tt.expected)
    }
  }
}

执行测试命令:

$ go test ./02_testing/test02
--- FAIL: TestIsPrime (0.00s)
    test02_test.go:25: IsPrime(7)=true,预期为 false
FAIL
FAIL    go.standard.library.study/02_testing/test02     0.037s
FAIL

上面测试中最后一个测试用例 错误的,所以执行测试时会返回测试不通过的结果,当然错误的原因并不是程序问题,而是测试用例的错误。

因为测试中使用的是t.Errorf,其中某个情况测试失败,并不会中止测试,其他测试用例会继续执行下去。在单元测试中,传递给测试函数的参数是*testing.T类型,它用于管理测试状态并支持格式化测试日志(测试日志会在执行测试的过程中不断累积,并在测试完成时输出到标准输出上)。

在一次测试中,测试函数执行结束返回,或者测试函数调用FailNow,Fatal,Fatalf,SkipNow,Skip,Skipf中的任意一个的时候,这次测试宣告结束,与Parallel方法一样,以上提及的这些方法只能在运行测试函数的goroutine中调用,而其他打印方法,比如Log,以及Error的变种,则可以在多个goroutine中同时调用。

下面总结了测试的几个方法的含义,当某个测试用例测试失败的时候,这些方法的后续动作分别如下:

Fail:记录失败信息,然后继续执行后续用例;

Failf:相比于前者多了个格式化输出;

FailNow:记录失败信息,所有测试中断;

Fatal:相当于Log+FailNow,会中断后续测试;

Fatalf:相比于前者多了个格式化输出;

Skip:不记录失败信息,中断后续测试;

Skipf:相比于前者多了个格式化输出;

SkipNow:不会记录失败的用例信息,然后终止测试;

Log:输出错误信息,在单元测试中,默认不输出成功的用例信息,不会中断后续测试;

Logf:相比于前者多了个格式化输出;

Error:相当于Log+Fail,不会中断后续测试;

Errorf:相比于前者多了个格式化输出;

在默认情况下,单元测试成功时,他们打印的信息不会输出,可以通过加上-v选项。

$ go test -v ./02_testing/test02
=== RUN   TestIsPrime
    test02_test.go:25: IsPrime(7)=true,预期为 false
--- FAIL: TestIsPrime (0.00s)
FAIL
FAIL    go.standard.library.study/02_testing/test02     0.022s
FAIL

3. 模拟测试 △

单元测试的原则,就是你所测试的函数方法,不受依赖环境的影响,比如网络访问等。但有时候运行单元测试的时候需要联网,而由于开发环境限制,不能联网,此时就需要进行模拟网络访问来完成测试了。

1. HTTP mock

针对模拟网络访问,标准库提供了一个httptest包,可以模拟HTTP 的网络调用,下面举个例子了解如何使用:

./testing/test03/test03.go

package main
import (
  "encoding/json"
  "net/http"
)
func Routers() {
  http.HandleFunc("/sendjson", SendJson)
}
// SendJson 发送JSON信息
func SendJson(rw http.ResponseWriter, r *http.Request) {
  u := struct {
    Name string
  }{"张三"}
  rw.Header().Set("Content-Type", "application/json")
  rw.WriteHeader(http.StatusOK)
  json.NewEncoder(rw).Encode(u)
}

非常简单,这里是一个/sendjson API,当访问这个API 的时候,会返回一个JSON 字符串。

现在对这个API服务进行测试,但又不能时时刻刻都启动服务,所以这里就用到了外部终端对API的网络访问请求:

./testing/test03/test03_test.go

package main
import (
  "log"
  "net/http"
  "net/http/httptest"
  "testing"
)
func init()  {
  Routers()
}
func TestSendJson(t *testing.T) {
  req, err := http.NewRequest(http.MethodGet, "/sendjson", nil)
  if err != nil {
    t.Fatal("创建Request失败!")
  }
  // ResponseRecorder 是 ResponseWriter的一个实现,
  // 它记录其突变,以便稍后在测试中检查。
  rw := httptest.NewRecorder()
  http.DefaultServeMux.ServeHTTP(rw, req)
  log.Println("code: ",rw.Code)
  log.Println("body: ",rw.Body.String())
}

执行测试命令:

$ go test -v ./02_testing/test03
=== RUN   TestSendJson
2022/07/16 10:54:24 code:  200
2022/07/16 10:54:24 body:{"Name":"张三"}
--- PASS: TestSendJson (0.02s)
PASS
ok      go.standard.library.study/02_testing/test03     0.041s

可以看到程序自动访问/sendjson API 的结果,并且没有启动任何HTTP服务就达到了目的。

这里主要利用·httptest.NewRecorder()创建一个http.ResponseWriter,模拟了真实服务端的响应,这种响应是通过调用http.DefaultServerMux.ServerHttp()方法触发的。

还有一个模拟调用的方法,是真的在测试机上模拟一个服务器,然后进行调试:

./testing/test03_test.go

package main
import (
  "io/ioutil"
  "log"
  "net/http"
  "net/http/httptest"
  "testing"
)
//...
func mockServer() *httptest.Server {
  sendJson := SendJson
    // 适配器转换
  return httptest.NewServer(http.HandlerFunc(sendJson))
}
func TestSendJson(t *testing.T) {
    // 创建一个模拟的服务器
  server := mockServer()
  defer server.Close()
  log.Println("server.URL: ", server.URL)
  // Get请求发往模拟服务器的地址
  resp, err := http.Get(server.URL)
  if err != nil {
    t.Fatal("创建Get请求失败!")
  }
  defer resp.Body.Close()
  log.Println("code: ", resp.StatusCode)
  json, err := ioutil.ReadAll(resp.Body)
  if err != nil {
    log.Fatal(err)
  }
  log.Printf("body:%s \n", json)
}

模拟服务器的创建使用的是httptest.NewServer函数,他接收一个http.Handler处理API请求的接口。代码示例中使用Handler的适配器模式,http.HandlerFunc是一个函数类型,实现了http.Hanler接口,注意这里的http.HandlerFunc(sendJson)是强制类型转换,不是函数的调用!!!

这个创建的模拟服务器,监听的是本机IP:127.0.0.1 ,端口时随机的。

接着发送Get请求的时候,不再发往/sendjson,而是模拟服务器的地址server.URL,剩下的就和访问正常的URL一样了,打印出结果即可;

执行测试命令:

$  go test -v ./02_testing/test03
=== RUN   TestSendJson
2022/07/16 10:54:24 code:  200
2022/07/16 10:54:24 body:{"Name":"张三"}
--- PASS: TestSendJson (0.02s)
PASS
ok      go.standard.library.study/02_testing/test03     (cached)

2. 数据库 mock

除了网络依赖之外,我们在开发中也会经常用到各种数据库,比如常见的MySQL和Redis等。该部分就分别举例来演示如何在编写单元测试的时候对MySQL和Redis进行mock。

mysql:go-sqlmock

sqlmock 是一个实现 sql/driver 的mock库。它不需要建立真正的数据库连接就可以在测试中模拟任何 sql 驱动程序的行为。使用它可以很方便的在编写单元测试的时候mock sql语句的执行结果。

1.安装

go get github.com/DATA-DOG/go-sqlmock

2.使用示例:

这里使用的是go-sqlmock官方文档中提供的基础示例代码。

在下面的代码中,我们实现了一个recordStats函数用来记录用户浏览商品时产生的相关数据。

具体实现的功能是在一个事务中进行以下两次SQL操作:

在products表中将当前商品的浏览次数+1

在product_viewers表中记录浏览当前商品的用户id

./02_testing/test07/test07.go

package main
import (
  "database/sql"
  "log"
)
// recordStats 记录用户浏览产品信息
func recordStats(db *sql.DB, userID, productID int64) (err error) {
  // 开启事务, 操作views和product_viewers两张表
  tx, err := db.Begin()
  if err != nil {
    log.Println(err)
    return err
  }
  // 没有错误就提交,否则回滚
  defer func() {
    switch err {
    case nil:
      err = tx.Commit()
    default:
      tx.Rollback()
    }
  }()
  // 更新products表
  updataSql := "UPDATE products SET views = views + 1"
  if _, err = tx.Exec(updataSql); err != nil {
    return err
  }
  // product_viewers表中插入一条数据
  insertSql := "INSERT INTO product_viewers (user_id,product_id) VALUES (?,?))"
  if _, err = tx.Exec(insertSql, userID, productID); err != nil {
    log.Printf("SQL Exec error:%v", err)
    return err
  }
  return err
}

./02_testing/test07/test07_test.go

package main
import (
  "fmt"
  "github.com/DATA-DOG/go-sqlmock"
  "testing"
)
// TestShouldUpdateStats sql执行成功的测试用例
func TestShouldUpdateStats(t *testing.T) {
  // mock一个*sql.DB对象,不需要连接真实的数据库
  db, mock, err := sqlmock.New()
  if err != nil {
    t.Fatalf("an error %s was not expected when opening a stub database connection", err.Error())
  }
  defer db.Close()
  mock.ExpectBegin()
  // 指定你期望(Expectations)执行的语句,以及假定的返回结果(WillReturnResult)。
  // 这里假定会返回(1, 1),也就是自增主键为1,1条影响结果
  mock.ExpectExec("UPDATE products").
    WillReturnResult(sqlmock.NewResult(1, 1))
  mock.ExpectExec("INSERT INTO product_viewers").
    WithArgs(2, 3). // 使用2 3作为参数
    WillReturnResult(sqlmock.NewResult(1, 1))
  mock.ExpectCommit()
  // 将mock的DB对象传入我们的函数中
  if err = recordStats(db, 2, 3); err != nil {
    t.Errorf("error was not expected while updating stats: %s", err)
  }
  // 确保期望的结果都满足
  if err := mock.ExpectationsWereMet(); err != nil {
    t.Errorf("there were unfulfilled expectations: %s", err)
  }
}
// TestShouldRollbackStatUpdatesOnFailure sql执行失败回滚的测试用例
func TestShouldRollbackStatUpdatesOnFailure(t *testing.T) {
  db, mock, err := sqlmock.New()
  if err != nil {
    t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
  }
  defer db.Close()
  mock.ExpectBegin()
  mock.ExpectExec("UPDATE products").WillReturnResult(sqlmock.NewResult(1, 1))
  // 与上面的成功测试不同 在于这里是 WillReturnError
  mock.ExpectExec("INSERT INTO product_viewers").
    WithArgs(2, 3).                           // 使用2 3作为参数
    WillReturnError(fmt.Errorf("some error")) // 允许为预期的数据库执行操作设置错误
  mock.ExpectRollback()
  // 将mock的DB对象传入我们的函数中
  if err = recordStats(db, 2, 3); err == nil {
    t.Errorf("was expecting an error, but there was none")
  }
  // 确保期望的结果都满足
  if err := mock.ExpectationsWereMet(); err != nil {
    t.Errorf("there were unfulfilled expectations: %s", err)
  }
}

上面的代码中,定义了一个执行成功的测试用例和一个执行失败回滚的测试用例,确保我们代码中的每个逻辑分支都能被测试到,提高单元测试覆盖率的同时也保证了代码的健壮性。

执行单元测试,看一下最终的测试结果:

$ go test -v ./02_testing/test07
=== RUN   TestShouldUpdateStats
--- PASS: TestShouldUpdateStats (0.00s)
=== RUN   TestShouldRollbackStatUpdatesOnFailure
2022/07/17 18:03:01 SQL Exec error:some error
--- PASS: TestShouldRollbackStatUpdatesOnFailure (0.01s)
PASS
ok      go.standard.library.study/02_testing/test07     (cached)

可以看到两个测试用例的结果都符合预期,单元测试通过。

在很多使用ORM工具的场景下,也可以使用go-sqlmock库mock数据库操作进行测试。

redis:miniredis

除了经常用到MySQL外,Redis在日常开发中也会经常用到。

**miniredis**是一个纯go实现的用于单元测试的redis server。它是一个简单易用的、基于内存的redis替代品,它具有真正的TCP接口,你可以把它当成是redis版本的net/http/httptest。

当我们为一些包含Redis操作的代码编写单元测试时就可以使用它来mock Redis操作。

1.安装:

go get -u github.com/alicebob/miniredis

2.使用示例:

这里以github.com/go-redis/redis库为例,编写了一个包含若干Redis操作的DoSomethingWithRedis函数。

02_testing/test08/test08.go

package test08
import (
  "github.com/go-redis/redis"
  "log"
  "strings"
  "time"
)
const (
  KeyValidWebsite = "app:valid:website:list"
)
func DoSomethingWithRedis(rdb *redis.Client, key string) bool {
  // 这里可以是对redis操作的一些逻辑
  // TODO返回一个非空的上下文。
  // 代码应该在不清楚要使用哪个上下文或者它还不可用的时候使用这个函数。
  //ctx:=context.TODO()
  // 判断成员元素是否是集合的成员
  if !rdb.SIsMember(KeyValidWebsite, key).Val() {
    return false
  }
  val, err := rdb.Get(key).Result()
  if err != nil {
    log.Println("no such key")
    return false
  }
  if !strings.HasPrefix(val, "https://") {
    val = "https://" + val
  }
  // 设置 blog key 5秒过期
  if err := rdb.Set("blog", val, 5*time.Second).Err(); err != nil {
    return false
  }
  return true
}

下面的代码是使用miniredis库为DoSomethingWithRedis函数编写的单元测试代码,其中miniredis不仅支持mock常用的Redis操作,还提供了很多实用的帮助函数,例如检查key的值是否与预期相等的s.CheckGet()和帮助检查key过期时间的s.FastForward()。

./02_testing/test08/test08_test.go

package test08
import (
  "github.com/alicebob/miniredis"
  "github.com/go-redis/redis"
  "testing"
  "time"
)
func TestDoSomethingWithRedis(t *testing.T) {
  // mock一个redis server
  mockRedisServer, err := miniredis.Run()
  if err != nil {
    t.Errorf("mock redis server error: %v", err)
  }
  defer mockRedisServer.Close()
  // 准备数据
  mockRedisServer.Set("q1mi", "liwenzhou.com")
  mockRedisServer.SetAdd(KeyValidWebsite, "q1mi")
  // 连接mock的redis server
  rdb := redis.NewClient(&redis.Options{
    Addr: mockRedisServer.Addr(), // mock redis server的地址
  })
  // 调用函数
  ok:=DoSomethingWithRedis(rdb,"qimi")
  if !ok {
    t.Fatal()
  }
  // 可以手动检查redis中的值是否复合预期
  if got, err := mockRedisServer.Get("blog"); err != nil || got != "https://liwenzhou.com" {
    t.Fatalf("'blog' has the wrong value")
  }
  // 也可以使用帮助工具检查
  mockRedisServer.CheckGet(t, "blog", "https://liwenzhou.com")
  // 过期检查
  mockRedisServer.FastForward(5 * time.Second) // 快进5秒
  if mockRedisServer.Exists("blog") {
    t.Fatal("'blog' should not have existed anymore")
  }
}

执行执行测试,查看单元测试结果:

$ go test -v ./02_testing/test08
=== RUN   TestDoSomethingWithRedis
--- PASS: TestDoSomethingWithRedis (0.00s)
PASS
ok      go.standard.library.study/02_testing/test08     0.559s

miniredis基本上支持绝大多数的Redis命令,大家可以通过查看文档了解更多用法。

当然除了使用miniredis搭建本地redis server这种方法外,还可以使用各种打桩工具对具体方法进行打桩。在编写单元测试时具体使用哪种mock方式还是要根据实际情况来决定。

4. 测试覆盖率

尽可能模拟更多的情况来测试代码的不同情况,但是有时候的确也有忘记测试的代码,这时候就需要测试覆盖率作为参考了。

由单元测试的代码,触发运行的被测试代码的占所有代码行数的比例,被称为测试覆盖率,代码覆盖率不一定完全精准,但是可以作为参考,可以有助于测试和预计覆盖率之间的差距,go test工具就提供了这样一个度量测试覆盖率的能力。

依旧使用之前的素数判断的程序./02_testing/test02.go(注意把7对应的值修改为true,不然无法通过测试),现在使用go test工具运行单元测试,和前几次不一样的是,要显示测试覆盖率,所以要多加一个参数-coverprofile,完整的命令为go test -v -coverprofile="./02_testing/c.out" ./02_testing,-coverprofile是指定生成的覆盖率文件,例子中是c.out,这个文件稍后会用到。

现在看终端输出,已经比刚才多出了一个覆盖率了:

$ go test -v -coverprofile="./02_testing/test02/c.out" ./02_testing/test02
=== RUN   TestIsPrime
--- PASS: TestIsPrime (0.00s)
PASS
coverage: 75.0% of statements
ok      go.standard.library.study/02_testing/test02     0.037s  coverage: 75.0% of statements

现在测试覆盖率为75.0%,还没有到100%,那么看看还有那些代码没有被测试到。


这就需要刚刚生成的测试覆盖率文件c.out生成的测试覆盖率报告了。生成报告使用Go提供的工具go tool cover -html=./02_testing/test02/c.out -o=tag.html,即可生成一个名字为tag.html的HTML格式的测试覆盖率报告,使用浏览器打开之后如下图:


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WXSBuTzM-1658054513453)(images/image-20220716110358669.png)]


这里有详细的信息告诉我们哪行代码被测试了,哪行代码没有被测试到。


可以看到标记为绿色的代码表示已经被测试了,标记为红色的表示还没有被测试到,现在根据没有被测试到的代码逻辑,完善单元测试代码即可(例如新增一个测试实例:{25, false},即可100%覆盖isPrime函数)

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
2天前
|
存储 Go 索引
go语言使用for循环遍历
go语言使用for循环遍历
16 7
|
5天前
|
存储 Go
go语言 遍历映射(map)
go语言 遍历映射(map)
18 2
|
6天前
|
Go 调度 开发者
Go语言中的并发编程:深入理解goroutines和channels####
本文旨在探讨Go语言中并发编程的核心概念——goroutines和channels。通过分析它们的工作原理、使用场景以及最佳实践,帮助开发者更好地理解和运用这两种强大的工具来构建高效、可扩展的应用程序。文章还将涵盖一些常见的陷阱和解决方案,以确保在实际应用中能够避免潜在的问题。 ####
|
6天前
|
测试技术 Go 索引
go语言使用 range 关键字遍历
go语言使用 range 关键字遍历
14 3
|
6天前
|
测试技术 Go 索引
go语言通过 for 循环遍历
go语言通过 for 循环遍历
16 3
|
8天前
|
安全 Go 数据处理
Go语言中的并发编程:掌握goroutine和channel的艺术####
本文深入探讨了Go语言在并发编程领域的核心概念——goroutine与channel。不同于传统的单线程执行模式,Go通过轻量级的goroutine实现了高效的并发处理,而channel作为goroutines之间通信的桥梁,确保了数据传递的安全性与高效性。文章首先简述了goroutine的基本特性及其创建方法,随后详细解析了channel的类型、操作以及它们如何协同工作以构建健壮的并发应用。此外,还介绍了select语句在多路复用中的应用,以及如何利用WaitGroup等待一组goroutine完成。最后,通过一个实际案例展示了如何在Go中设计并实现一个简单的并发程序,旨在帮助读者理解并掌
|
7天前
|
Go 索引
go语言按字符(Rune)遍历
go语言按字符(Rune)遍历
21 3
|
11天前
|
Go API 数据库
Go 语言中常用的 ORM 框架,如 GORM、XORM 和 BeeORM,分析了它们的特点、优势及不足,并从功能特性、性能表现、易用性和社区活跃度等方面进行了比较,旨在帮助开发者根据项目需求选择合适的 ORM 框架。
本文介绍了 Go 语言中常用的 ORM 框架,如 GORM、XORM 和 BeeORM,分析了它们的特点、优势及不足,并从功能特性、性能表现、易用性和社区活跃度等方面进行了比较,旨在帮助开发者根据项目需求选择合适的 ORM 框架。
35 4
|
11天前
|
缓存 监控 前端开发
在 Go 语言中实现 WebSocket 实时通信的应用,包括 WebSocket 的简介、Go 语言的优势、基本实现步骤、应用案例、注意事项及性能优化策略,旨在帮助开发者构建高效稳定的实时通信系统
本文深入探讨了在 Go 语言中实现 WebSocket 实时通信的应用,包括 WebSocket 的简介、Go 语言的优势、基本实现步骤、应用案例、注意事项及性能优化策略,旨在帮助开发者构建高效稳定的实时通信系统。
45 1
|
9天前
|
存储 Go PHP
Go语言中的加解密利器:go-crypto库全解析
在软件开发中,数据安全和隐私保护至关重要。`go-crypto` 是一个专为 Golang 设计的加密解密工具库,支持 AES 和 RSA 等加密算法,帮助开发者轻松实现数据的加密和解密,保障数据传输和存储的安全性。本文将详细介绍 `go-crypto` 的安装、特性及应用实例。
28 0