最近做了一个需求,在程序一开始的时候抓网卡流量
然后通过一个接口可以修改抓流量的网卡
但是之前我们抓网卡流量的方法是在一开始就运行了,如何实现任务的停止和启动呢
很多时候我们执行一个任务要么就是在程序一开始就去启动
例如这样
package main
import (
"os"
"os/exec"
)
// 任务的函数
func run() {
//cmder := "ping baidu.com -n 100"
cmd := exec.Command("ping", "baidu.com", "-n", "100")
cmd.Stdout = os.Stdout
cmd.Run()
}
func main() {
// 在一开始去执行任务
run()
}
如果需要动态的启停这个任务,应该怎么实现呢
我们来实操一下
我们执行的这种任务有两种情况
- 有自带context的,比如redis命令执行,cmd命令执行,这种实现起来很简单
- 没有自带context的,就需要我们自己实现context逻辑
我们先封装一个web来实现任务的停止和启动
package main
import (
"context"
"fmt"
"github.com/gin-gonic/gin"
"os"
"os/exec"
)
// 任务的函数
func run() {
//cmder := "ping baidu.com -n 100"
cmd := exec.Command("ping", "baidu.com", "-n", "100")
cmd.Stdout = os.Stdout
cmd.Run()
}
func main() {
r := gin.Default()
r.GET("/start", func(context *gin.Context) {
context.String(200, "任务启动成功")
})
r.GET("/stop", func(context *gin.Context) {
context.String(200, "任务停止成功")
})
r.Run(":8080")
}
自带context的情况
这种实现任务的停止和启动就很简单了
直接使用context.WithCancel返回的cancel取消函数就可以取消任务了
package main
import (
"context"
"fmt"
"github.com/gin-gonic/gin"
"os"
"os/exec"
)
var cancel context.CancelFunc
// 任务启动
func startTask() {
ctx, _cancel := context.WithCancel(context.Background())
cancel = _cancel
cmd := exec.CommandContext(ctx, "ping", "baidu.com", "-n", "100")
cmd.Stdout = os.Stdout
fmt.Println("开启任务")
cmd.Run()
}
func stopTask() {
cancel()
fmt.Println("任务停止")
}
func main() {
r := gin.Default()
r.GET("/start", func(context *gin.Context) {
go startTask()
context.String(200, "任务启动成功")
})
r.GET("/stop", func(context *gin.Context) {
stopTask()
context.String(200, "任务停止成功")
})
r.Run(":8080")
}
自己实现context
有些第三方库,或者自己编写的任务可能没有context参数
这个就需要我们自己实现context的功能了
如果是执行系统命令的,我们可以使用exec自带的kill把进程给结束就可以了
package main
import (
"context"
"fmt"
"github.com/gin-gonic/gin"
"os"
"os/exec"
)
var cancel context.CancelFunc
var ctx context.Context
// 任务启动
func startTask() {
ctx, cancel = context.WithCancel(context.Background())
cmd := exec.Command("ping", "baidu.com", "-n", "100")
go func() {
select {
case <-ctx.Done():
cmd.Process.Kill()
fmt.Println("任务被手动停止")
return
}
}()
cmd.Stdout = os.Stdout
fmt.Println("开启任务")
cmd.Run()
}
func stopTask() {
cancel()
fmt.Println("任务停止")
}
func main() {
r := gin.Default()
r.GET("/start", func(context *gin.Context) {
go startTask()
context.String(200, "任务启动成功")
})
r.GET("/stop", func(context *gin.Context) {
stopTask()
context.String(200, "任务停止成功")
})
r.Run(":8080")
}
如果不是执行系统命令这种,比如就是代码里面的循环,就要麻烦一点了
有两种方案,一是使用context,二是使用信号channel
使用context
package main
import (
"context"
"fmt"
"github.com/gin-gonic/gin"
"time"
)
var cancel context.CancelFunc
var ctx context.Context
// 任务启动
func startTask() {
ctx, cancel = context.WithCancel(context.Background())
fmt.Println("开启任务")
for i := 0; i < 100; i++ {
select {
case <-ctx.Done():
fmt.Println("任务被手动停止")
return
default:
}
fmt.Println("任务执行中...", i)
time.Sleep(1 * time.Second)
}
}
func stopTask() {
cancel()
fmt.Println("任务停止")
}
func main() {
r := gin.Default()
r.GET("/start", func(context *gin.Context) {
go startTask()
context.String(200, "任务启动成功")
})
r.GET("/stop", func(context *gin.Context) {
stopTask()
context.String(200, "任务停止成功")
})
r.Run(":8080")
}
使用channel
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"time"
)
var quit chan struct{}
// 任务启动
func startTask() {
quit = make(chan struct{})
fmt.Println("开启任务")
for i := 0; i < 100; i++ {
select {
case <-quit:
fmt.Println("任务被手动停止")
return
default:
}
fmt.Println("任务执行中...", i)
time.Sleep(1 * time.Second)
}
}
func stopTask() {
close(quit)
fmt.Println("任务停止")
}
func main() {
r := gin.Default()
r.GET("/start", func(context *gin.Context) {
go startTask()
context.String(200, "任务启动成功")
})
r.GET("/stop", func(context *gin.Context) {
stopTask()
context.String(200, "任务停止成功")
})
r.Run(":8080")
}