golang 创建一个简单的连接池,减少频繁的创建与关闭
发布时间:2020-12-16 18:02:30 所属栏目:大数据 来源:网络整理
导读:一、连接池的描述图片如下: 二、连接池代码如下: package main;import ("time""sync""errors""net""fmt")//频繁的创建和关闭连接,对系统会造成很大负担//所以我们需要一个池子,里面事先创建好固定数量的连接资源,需要时就取,不需要就放回池中。//但是
一、连接池的描述图片如下: 二、连接池代码如下: package main;
import (
"time"
"sync"
"errors"
"net"
"fmt"
)
//频繁的创建和关闭连接,对系统会造成很大负担
//所以我们需要一个池子,里面事先创建好固定数量的连接资源,需要时就取,不需要就放回池中。
//但是连接资源有一个特点,我们无法保证连接长时间会有效。
//比如,网络原因,人为原因等都会导致连接失效。
//所以我们设置一个超时时间,如果连接时间与当前时间相差超过超时时间,那么就关闭连接。
//只要类型实现了ConnRes接口中的方法,就认为是一个连接资源类型
type ConnRes interface {
Close() error;
}
//工厂方法,用于创建连接资源
type Factory func() (ConnRes,error)
//连接
type Conn struct {
conn ConnRes;
//连接时间
time time.Time;
}
//连接池
type ConnPool struct {
//互斥锁,保证资源安全
mu sync.Mutex;
//通道,保存所有连接资源
conns chan *Conn;
//工厂方法,创建连接资源
factory Factory;
//判断池是否关闭
closed bool;
//连接超时时间
connTimeOut time.Duration;
}
//创建一个连接资源池
func NewConnPool(factory Factory,cap int,connTimeOut time.Duration) (*ConnPool,error) {
if cap <= 0 {
return nil,errors.New("cap不能小于0");
}
if connTimeOut <= 0 {
return nil,errors.New("connTimeOut不能小于0");
}
cp := &ConnPool{
mu: sync.Mutex{},conns: make(chan *Conn,cap),factory: factory,closed: false,connTimeOut: connTimeOut,};
for i := 0; i < cap; i++ {
//通过工厂方法创建连接资源
connRes,err := cp.factory();
if err != nil {
cp.Close();
return nil,errors.New("factory出错");
}
//将连接资源插入通道中
cp.conns <- &Conn{conn: connRes,time: time.Now()};
}
return cp,nil;
}
//获取连接资源
func (cp *ConnPool) Get() (ConnRes,error) {
if cp.closed {
return nil,errors.New("连接池已关闭");
}
for {
select {
//从通道中获取连接资源
case connRes,ok := <-cp.conns:
{
if !ok {
return nil,errors.New("连接池已关闭");
}
//判断连接中的时间,如果超时,则关闭
//继续获取
if time.Now().Sub(connRes.time) > cp.connTimeOut {
connRes.conn.Close();
continue;
}
return connRes.conn,nil;
}
default:
{
//如果无法从通道中获取资源,则重新创建一个资源返回
connRes,err := cp.factory();
if err != nil {
return nil,err;
}
return connRes,nil;
}
}
}
}
//连接资源放回池中
func (cp *ConnPool) Put(conn ConnRes) error {
if cp.closed {
return errors.New("连接池已关闭");
}
select {
//向通道中加入连接资源
case cp.conns <- &Conn{conn: conn,time: time.Now()}:
{
return nil;
}
default:
{
//如果无法加入,则关闭连接
conn.Close();
return errors.New("连接池已满");
}
}
}
//关闭连接池
func (cp *ConnPool) Close() {
if cp.closed {
return;
}
cp.mu.Lock();
cp.closed = true;
//关闭通道
close(cp.conns);
//循环关闭通道中的连接
for conn := range cp.conns {
conn.conn.Close();
}
cp.mu.Unlock();
}
//返回池中通道的长度
func (cp *ConnPool) len() int {
return len(cp.conns);
}
func main() {
cp,_ := NewConnPool(func() (ConnRes,error) {
return net.Dial("tcp",":8080");
},10,time.Second*10);
//获取资源
conn1,_ := cp.Get();
conn2,_ := cp.Get();
//这里连接池中资源大小为8
fmt.Println("cp len : ",cp.len());
conn1.(net.Conn).Write([]byte("hello"));
conn2.(net.Conn).Write([]byte("world"));
buf := make([]byte,1024);
n,_ := conn1.(net.Conn).Read(buf);
fmt.Println("conn1 read : ",string(buf[:n]));
n,_ = conn2.(net.Conn).Read(buf);
fmt.Println("conn2 read : ",string(buf[:n]));
//等待15秒
time.Sleep(time.Second * 15);
//我们再从池中获取资源
conn3,_ := cp.Get();
//这里显示为0,因为池中的连接资源都超时了
fmt.Println("cp len : ",cp.len());
conn3.(net.Conn).Write([]byte("test"));
n,_ = conn3.(net.Conn).Read(buf);
fmt.Println("conn3 read : ",string(buf[:n]));
//把三个连接资源放回池中
cp.Put(conn1);
cp.Put(conn2);
cp.Put(conn3);
//这里显示为3
fmt.Println("cp len : ",cp.len());
cp.Close();
}
三、8080服务端代码如下: package main;
import (
"net"
"io"
"log"
)
func handler(conn net.Conn) {
for {
io.Copy(conn,conn);
}
}
func main() {
lis,err := net.Listen("tcp",":8080");
if err != nil {
log.Fatal(err);
}
for {
conn,err := lis.Accept();
if err != nil {
continue;
}
go handler(conn);
}
}
测试结果如下: (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
相关内容