前言
在实际的工程中,经常会遇到访问请求流量达到服务器,服务器需要处理的任务比较复杂(比如,加载镜像,启动任务等)。但是,如果等服务端处理完成后,会导致Http连接超时,从而造成访问失败。因此,我们常用的一种办法是先返回给客户端需要的反馈数据,然后异步处理比较耗时的过程,之后再推送处理结果或者更新数据库等相应的状态。
实验
在web开发中,Go的应用也比较广泛,比较常用的有Beego,GIN,Lris,Echo等,本次实验我们采用GIN来实现,其他的实现方式也不会相差太多。
代码
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"log"
"net/http"
"strconv"
"time"
pm "web/pg_manager"
)
var mydb pm.MyDB
var ChanPool chan byte
func init() {
ChanPool = make(chan byte, 2)
mydb.New(20)
}
func main() {
defer mydb.Close()
r := gin.Default()
v1 := r.Group("/v1.0/")
v1.GET("/images", getImages)
r.Run(":9051")
}
func getImages(c *gin.Context) {
select {
case ChanPool <- '1':
sql := "select row_to_json(t.*) from (select * from imagetab where user_id=17) t"
results := mydb.ReadMany(sql)
go loop() //模拟耗时
c.JSON(200, results)
default:
c.JSON(500, "服务器负载严重,请稍后再试...")
}
}
func loop() {
for i := 0; i < 10; i++ {
time.Sleep(time.Second)
fmt.Println(i)
}
<-pm.ChanPool
}
- 代码实现的功能:获取image列表,但是在获取时,模拟一个耗时比较长的操作。设置的chan大小为2,可以连续处理2个http请求,如果超过2个,会返回一个状态为500,并且反馈信息为”服务器负载严重,请稍后再试...“
- 使用chan来记录耗时请求的访问的次数
- 如果耗时操作完成,则从chan中释放一个数据,其他的请求就可以进来
- 如果chan中的数据达到了2个,之后在插入到chan中的数据会阻塞,此时从执行
select中的default。
模拟
$ for i in $(seq 1 3); do curl http://localhost:9051/v1.0/images;done
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 4 100 4 0 0 125 0 --:--:-- --:--:-- --:--:-- 125null
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 4 100 4 0 0 86 0 --:--:-- --:--:-- --:--:-- 86null
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 42 100 42 0 0 531 0 --:--:-- --:--:-- --:--:-- 531"服务器负载严重,请稍后再试..."
前面两次可以访问成功,第三次的时候访问失败