[Go] golang缓冲通道实现资源池

go的pool资源池:
1.当有多个并发请求的时候,比如需要查询数据库
2.先创建一个2个容量的数据库连接资源池
3.当一个请求过来的时候,去资源池里请求连接资源,肯定是空的就创建一个连接,执行查询,结束后放入了资源池里
4.当第二个请求过来的时候,也是去资源池请求连接资源,就直接在池中拿过来一个连接进行查询
5.当并发大的时候,资源池里面没有足够连接资源,就会不停创建新资源,放入池里面的时候,也会放不进去,就主动关闭掉这个资源
6.这里的资源池实质上是一个缓冲通道,里面放着连接资源

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package main
 
import (
    "errors"
    "io"
    "log"
    "math/rand"
    "sync"
    "sync/atomic"
    "time"
)
 
//定义一个结构体,这个实体类型可以作为整体单元被复制,可以作为参数或返回值,或被存储到数组
type Pool struct {
    //定义成员,互斥锁类型
    m sync.Mutex
    //定义成员,通道类型,通道传递的是io.Closer类型
    resources chan io.Closer
    //定义工厂成员,类型是func()(io.Closer,error)
    //error是预定义类型,实际上是个interface接口类型
    factory func() (io.Closer, error)
    closed  bool
}
 
//定义变量,函数返回的是error类型
var ErrPoolClosed = errors.New("池已经关闭了")
 
//定义New方法,创建一个池,返回的是Pool类型的指针
//传入的参数是个函数类型func(io.Closer,error)和池的大小
func New(fn func() (io.Closer, error), size uint) (*Pool, error) {
    //使用结构体字面值给结构体成员赋值
    myPool := Pool{
        factory:   fn,
        resources: make(chan io.Closer, size),
    }
    //返回两个返回值
    return &myPool, nil
}
 
//从池中请求获取一个资源,给Pool类型定义的方法
//返回的值是io.Closer类型
func (p *Pool) Acquire() (io.Closer, error) {
    //基于select的多路复用
    //select会等待case中有能够执行的,才会去执行,等待其中一个能执行就执行
    //default分支会在所有case没法执行时,默认执行,也叫轮询channel
    select {
    case r, _ := <-p.resources:
        log.Printf("请求资源:来自通道 %d", r.(*dbConn).ID)
        return r, nil
    //如果缓冲通道中没有了,就会执行这里
    default:
        log.Printf("请求资源:创建新资源")
        return p.factory()
    }
}
 
//将一个使用后的资源放回池
//传入的参数是io.Closer类型
func (p *Pool) Release(r io.Closer) {
    //使用mutex互斥锁
    p.m.Lock()
    //解锁
    defer p.m.Unlock()
    //如果池都关闭了
    if p.closed {
        //关掉资源
        r.Close()
        return
    }
    //select多路选择
    //如果放回通道的时候满了,就关闭这个资源
    select {
    case p.resources <- r:
        log.Printf("释放资源:放入通道 %d", r.(*dbConn).ID)
    default:
        log.Printf("释放资源:关闭资源%d", r.(*dbConn).ID)
        r.Close()
    }
}
 
//关闭资源池,关闭通道,将通道中的资源关掉
func (p *Pool) Close() {
    p.m.Lock()
    defer p.m.Unlock()
    p.closed = true
    //先关闭通道再清空资源
    close(p.resources)
    //清空并关闭资源
    for r := range p.resources {
        r.Close()
    }
}
 
//定义全局常量
const (
    maxGoroutines = 20 //使用25个goroutine模拟同时的连接请求
    poolSize      = 2  //资源池中的大小
)
 
//定义结构体,模拟要共享的资源
type dbConn struct {
    //定义成员
    ID int32
}
 
//dbConn实现io.Closer接口
func (db *dbConn) Close() error {
    return nil
}
 
var idCounter int32 //定义一个全局的共享的变量,更新时用原子函数锁住
//定义方法,创建dbConn实例
//返回的是io.Closer类型和error类型
func createConn() (io.Closer, error) {
    //原子函数锁住,更新加1
    id := atomic.AddInt32(&idCounter, 1)
    log.Printf("创建新资源: %d", id)
    return &dbConn{id}, nil
}
func main() {
    //计数信号量
    var wg sync.WaitGroup
    //同时并发的数量
    wg.Add(maxGoroutines)
    myPool, _ := New(createConn, poolSize)
    //开25个goroutine同时查询
    for i := 0; i < maxGoroutines; i++ {
        //模拟请求
        time.Sleep(time.Duration(rand.Intn(2)) * time.Second)
        go func(gid int) {
            execQuery(gid, myPool)
            wg.Done()
        }(i)
    }
    //等待上面开的goroutine结束
    wg.Wait()
    myPool.Close()
}
 
//定义一个查询方法,参数是当前gorotineId和资源池
func execQuery(goroutineId int, pool *Pool) {
    //从池里请求资源,第一次肯定是没有的,就会创建一个dbConn实例
    conn, _ := pool.Acquire()
    //将创建的dbConn实例放入了资源池的缓冲通道里
    defer pool.Release(conn)
    //睡眠一下,模拟查询过程
    time.Sleep(time.Duration(rand.Intn(10)) * time.Second)
    log.Printf("执行查询...协程ID [%d] 资源ID [%d]", goroutineId, conn.(*dbConn).ID)
}

 

  

posted @   唯一客服系统开发笔记  阅读(1216)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
历史上的今天:
2016-02-23 [android] 点击事件的四种写法
点击右上角即可分享
微信分享提示
1
chat with us