1. 简单实现抽奖
- 安装并创建好iris测试项目
在测试项目目录下创建main.go文件,内容如下
/**
* curl http://localhost:8080/
* curl --data="user=user1,user2" http://localhost:8080/import
* curl http://localhost:8080/lucky
*/
package main
import (
"fmt"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/mvc"
"math/rand"
"strings"
"time"
)
var userList []string
type lotteryController struct {
Ctx iris.Context
}
func newApp() *iris.Application {
app := iris.New()
mvc.New(app.Party("/")).Handle(&lotteryController{})
return app
}
func main() {
app := newApp()
userList = []string{}
app.Run(iris.Addr(":8080"))
}
func (c *lotteryController) Get() string {
count := len(userList)
return fmt.Sprintf("当前总共参加抽奖的用户数: %d\n", count)
}
func (c *lotteryController) PostImport() string {
strUsers := c.Ctx.FormValue("users")
users := strings.Split(strUsers, ",")
count1 := len(userList)
for _, u := range users {
u = strings.TrimSpace(u)
if len(u) > 0 {
userList = append(userList, u)
}
}
count2 := len(userList)
return fmt.Sprintf("当前总共参与抽奖的用户数: %d,成功导入的用户数:%d\n",
count2, count2 - count1)
}
func (c *lotteryController) GetLucky() string {
count := len(userList)
if count > 1 {
seed := time.Now().UnixNano()
index := rand.New(rand.NewSource(seed)).Int31n(int32(count))
user := userList[index]
userList = append(userList[0:index], userList[index+1:]...)
return fmt.Sprintf("当前中奖用户:%s, 剩余用户数:%d\n", user , count-1)
} else if count == 1 {
user := userList[0]
return fmt.Sprintf("当前中奖用户:%s, 剩余用户数:%d\n", user , count-1)
} else {
return fmt.Sprintf("当前已经没有参与用户,请先通过 /import 导入用户\n")
}
}
测试请求当前总共参加抽奖的用户数
测试请求导入用户
测试请求抽奖
2. 写个单元测试简单测试下并发
同目录下创建main_test.go文件,内容如下
package main
import (
"fmt"
"github.com/kataras/iris/v12/httptest"
"sync"
"testing"
)
func TestMVC(t *testing.T) {
e := httptest.New(t, newApp())
/**
* WaitGroup 对象内部有一个计数器,最初从0开始,它有三个方法:Add(), Done(), Wait()
* 用来控制计数器的数量。
* Add(n) 把计数器设置为n ,Done() 每次把计数器-1 ,wait() 会阻塞代码的运行,直到计数器地值减为0。
*/
var wg sync.WaitGroup
e.GET("/").Expect().Status(httptest.StatusOK).Body().Equal("当前总共参加抽奖的用户数: 0\n")
for i := 0; i < 100; i++ {
wg.Add(1)
go func(i int) {
/**
* defer 语句会将其后面跟随的语句进行延迟处理,
* 在 defer 归属的函数即将返回时,将延迟处理的语句按 defer 的逆序进行执行,
* 也就是说,先被 defer 的语句最后被执行,最后被 defer 的语句,最先被执行。
*/
defer wg.Done()
e.POST("/import").WithFormField("users", fmt.Sprintf("test_u%d", i)).Expect().Status(httptest.StatusOK)
}(i)
}
wg.Wait()
e.GET("/").Expect().Status(httptest.StatusOK).Body().Equal("当前总共参加抽奖的用户数: 100\n")
e.GET("/lucky").Expect().Status(httptest.StatusOK)
e.GET("/").Expect().Status(httptest.StatusOK).Body().Equal("当前总共参加抽奖的用户数: 99\n")
}
执行程序:
发现存在线程安全性问题,没有导入100个用户的情况,因此需要优化下代码,为了实现线程安全,引入锁的使用,main.go的文件内容修改如下:
/**
* curl http://localhost:8080/
* curl --data="user=user1,user2" http://localhost:8080/import
* curl http://localhost:8080/lucky
*/
package main
import (
"fmt"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/mvc"
"math/rand"
"strings"
"sync"
"time"
)
var userList []string
var mu sync.Mutex
type lotteryController struct {
Ctx iris.Context
}
func newApp() *iris.Application {
app := iris.New()
mvc.New(app.Party("/")).Handle(&lotteryController{})
return app
}
func main() {
app := newApp()
userList = []string{}
mu = sync.Mutex{}//初始化互斥锁
app.Run(iris.Addr(":8080"))
}
func (c *lotteryController) Get() string {
count := len(userList)
return fmt.Sprintf("当前总共参加抽奖的用户数: %d\n", count)
}
func (c *lotteryController) PostImport() string {
strUsers := c.Ctx.FormValue("users")
users := strings.Split(strUsers, ",")
mu.Lock()
defer mu.Unlock()
count1 := len(userList)
for _, u := range users {
u = strings.TrimSpace(u)
if len(u) > 0 {
userList = append(userList, u)
}
}
count2 := len(userList)
return fmt.Sprintf("当前总共参与抽奖的用户数: %d,成功导入的用户数:%d\n",
count2, count2 - count1)
}
func (c *lotteryController) GetLucky() string {
mu.Lock()
defer mu.Unlock()
count := len(userList)
if count > 1 {
seed := time.Now().UnixNano()
index := rand.New(rand.NewSource(seed)).Int31n(int32(count))
user := userList[index]
userList = append(userList[0:index], userList[index+1:]...)
return fmt.Sprintf("当前中奖用户:%s, 剩余用户数:%d\n", user , count-1)
} else if count == 1 {
user := userList[0]
return fmt.Sprintf("当前中奖用户:%s, 剩余用户数:%d\n", user , count-1)
} else {
return fmt.Sprintf("当前已经没有参与用户,请先通过 /import 导入用户\n")
}
}
其中对到导入和抽奖两个方法加上了互斥锁,因为这两个方法会对 userList 修改值,因此需要上锁
再次测试:
注:代码源自于慕课大佬视频
网友评论