github.com/jtzjtz/kit@v1.0.2/conn/grpc_pool/grpc_pool.go (about)

     1  package grpc_pool
     2  
     3  import (
     4  	"context"
     5  	"google.golang.org/grpc/connectivity"
     6  	"sync"
     7  	"time"
     8  
     9  	"google.golang.org/grpc"
    10  )
    11  
    12  //GRPCPool conn info
    13  type GRPCPool struct {
    14  	Mu          sync.Mutex
    15  	IdleTimeout time.Duration
    16  	conns       chan *grpcIdleConn
    17  	factory     func() (*grpc.ClientConn, error)
    18  	close       func(*grpc.ClientConn) error
    19  	maxConns    int
    20  	minConns    int
    21  }
    22  
    23  type grpcIdleConn struct {
    24  	conn *grpc.ClientConn
    25  	t    time.Time
    26  }
    27  
    28  //Get get from conn
    29  func (c *GRPCPool) Get() (*grpc.ClientConn, error) {
    30  	c.Mu.Lock()
    31  	conns := c.conns
    32  	c.Mu.Unlock()
    33  
    34  	if conns == nil {
    35  		return nil, errClosed
    36  	}
    37  	for {
    38  		select {
    39  		case wrapConn := <-conns:
    40  			if wrapConn == nil {
    41  				return nil, errClosed
    42  			}
    43  			//判断是否超时,超时则丢弃
    44  			if timeout := c.IdleTimeout; timeout > 0 {
    45  				if wrapConn.t.Add(timeout).Before(time.Now()) {
    46  					//丢弃并关闭该链接
    47  					c.close(wrapConn.conn)
    48  					continue
    49  				}
    50  			}
    51  			if wrapConn.conn.GetState() != connectivity.Ready {
    52  				c.close(wrapConn.conn)
    53  				continue
    54  			}
    55  			return wrapConn.conn, nil
    56  		default:
    57  			if c.IdleCount() >= c.maxConns {
    58  				return nil, errTooManyConns
    59  			}
    60  			conn, err := c.factory()
    61  			if err != nil {
    62  				return nil, err
    63  			}
    64  			return conn, nil
    65  		}
    66  	}
    67  }
    68  
    69  //Put put back to conn
    70  func (c *GRPCPool) Put(conn *grpc.ClientConn) error {
    71  	if conn == nil {
    72  		return errRejected
    73  	}
    74  
    75  	c.Mu.Lock()
    76  	defer c.Mu.Unlock()
    77  
    78  	if c.conns == nil {
    79  		return c.close(conn)
    80  	}
    81  
    82  	select {
    83  	case c.conns <- &grpcIdleConn{conn: conn, t: time.Now()}:
    84  		return nil
    85  	default:
    86  		//连接池已满,直接关闭该链接
    87  		return c.close(conn)
    88  	}
    89  }
    90  
    91  //Close close conn
    92  func (c *GRPCPool) Close() {
    93  	c.Mu.Lock()
    94  	conns := c.conns
    95  	c.conns = nil
    96  	c.factory = nil
    97  	closeFun := c.close
    98  	c.close = nil
    99  	c.Mu.Unlock()
   100  
   101  	if conns == nil {
   102  		return
   103  	}
   104  
   105  	close(conns)
   106  	for wrapConn := range conns {
   107  		closeFun(wrapConn.conn)
   108  	}
   109  }
   110  
   111  //IdleCount idle connection count
   112  func (c *GRPCPool) IdleCount() int {
   113  	c.Mu.Lock()
   114  	conns := c.conns
   115  	c.Mu.Unlock()
   116  	return len(conns)
   117  }
   118  
   119  //
   120  func (c *GRPCPool) CheckConn() {
   121  	for {
   122  		if len(c.conns) < c.minConns {
   123  			add := c.minConns - len(c.conns)
   124  			for i := 0; i < add; i++ {
   125  				conn, _ := c.factory()
   126  				c.Put(conn)
   127  			}
   128  		}
   129  		if len(c.conns) > c.minConns {
   130  			disc := len(c.conns) - c.minConns
   131  			c.Mu.Lock()
   132  			conns := c.conns
   133  			c.Mu.Unlock()
   134  			for i := 0; i < disc; i++ {
   135  				wrapConn := <-conns
   136  				c.close(wrapConn.conn)
   137  			}
   138  		}
   139  		time.Sleep(time.Second * 10)
   140  	}
   141  }
   142  
   143  //NewGRPCPool init grpc conn
   144  func NewGRPCPool(o *Options, dialOptions ...grpc.DialOption) (*GRPCPool, error) {
   145  	if err := o.validate(); err != nil {
   146  		return nil, err
   147  	}
   148  
   149  	//init conn
   150  	pool := &GRPCPool{
   151  		conns: make(chan *grpcIdleConn, o.MaxCap),
   152  		factory: func() (*grpc.ClientConn, error) {
   153  			target := o.nextTarget()
   154  			if target == "" {
   155  				return nil, errTargets
   156  			}
   157  
   158  			ctx, cancel := context.WithTimeout(context.Background(), o.DialTimeout)
   159  			defer cancel()
   160  
   161  			return grpc.DialContext(ctx, target, dialOptions...)
   162  		},
   163  		close:       func(v *grpc.ClientConn) error { return v.Close() },
   164  		IdleTimeout: o.IdleTimeout,
   165  		maxConns:    o.MaxCap,
   166  		minConns:    o.InitCap,
   167  	}
   168  
   169  	//danamic update targets
   170  	o.update()
   171  
   172  	//init make conns
   173  	for i := 0; i < o.InitCap; i++ {
   174  		conn, err := pool.factory()
   175  		if err != nil {
   176  			pool.Close()
   177  			return nil, err
   178  		}
   179  		pool.conns <- &grpcIdleConn{conn: conn, t: time.Now()}
   180  	}
   181  	go pool.CheckConn()
   182  	return pool, nil
   183  }