github.com/annwntech/go-micro/v2@v2.9.5/client/grpc/grpc_pool.go (about)

     1  package grpc
     2  
     3  import (
     4  	"sync"
     5  	"time"
     6  
     7  	"google.golang.org/grpc"
     8  	"google.golang.org/grpc/connectivity"
     9  )
    10  
    11  type pool struct {
    12  	size int
    13  	ttl  int64
    14  
    15  	//  max streams on a *poolConn
    16  	maxStreams int
    17  	//  max idle conns
    18  	maxIdle int
    19  
    20  	sync.Mutex
    21  	conns map[string]*streamsPool
    22  }
    23  
    24  type streamsPool struct {
    25  	//  head of list
    26  	head *poolConn
    27  	//  busy conns list
    28  	busy *poolConn
    29  	//  the siza of list
    30  	count int
    31  	//  idle conn
    32  	idle int
    33  }
    34  
    35  type poolConn struct {
    36  	//  grpc conn
    37  	*grpc.ClientConn
    38  	err  error
    39  	addr string
    40  
    41  	//  pool and streams pool
    42  	pool    *pool
    43  	sp      *streamsPool
    44  	streams int
    45  	created int64
    46  
    47  	//  list
    48  	pre  *poolConn
    49  	next *poolConn
    50  	in   bool
    51  }
    52  
    53  func newPool(size int, ttl time.Duration, idle int, ms int) *pool {
    54  	if ms <= 0 {
    55  		ms = 1
    56  	}
    57  	if idle < 0 {
    58  		idle = 0
    59  	}
    60  	return &pool{
    61  		size:       size,
    62  		ttl:        int64(ttl.Seconds()),
    63  		maxStreams: ms,
    64  		maxIdle:    idle,
    65  		conns:      make(map[string]*streamsPool),
    66  	}
    67  }
    68  
    69  func (p *pool) getConn(addr string, opts ...grpc.DialOption) (*poolConn, error) {
    70  	now := time.Now().Unix()
    71  	p.Lock()
    72  	sp, ok := p.conns[addr]
    73  	if !ok {
    74  		sp = &streamsPool{head: &poolConn{}, busy: &poolConn{}, count: 0, idle: 0}
    75  		p.conns[addr] = sp
    76  	}
    77  	//  while we have conns check streams and then return one
    78  	//  otherwise we'll create a new conn
    79  	conn := sp.head.next
    80  	for conn != nil {
    81  		//  check conn state
    82  		// https://github.com/grpc/grpc/blob/master/doc/connectivity-semantics-and-api.md
    83  		switch conn.GetState() {
    84  		case connectivity.Connecting:
    85  			conn = conn.next
    86  			continue
    87  		case connectivity.Shutdown:
    88  			next := conn.next
    89  			if conn.streams == 0 {
    90  				removeConn(conn)
    91  				sp.idle--
    92  			}
    93  			conn = next
    94  			continue
    95  		case connectivity.TransientFailure:
    96  			next := conn.next
    97  			if conn.streams == 0 {
    98  				removeConn(conn)
    99  				conn.ClientConn.Close()
   100  				sp.idle--
   101  			}
   102  			conn = next
   103  			continue
   104  		case connectivity.Ready:
   105  		case connectivity.Idle:
   106  		}
   107  		//  a old conn
   108  		if now-conn.created > p.ttl {
   109  			next := conn.next
   110  			if conn.streams == 0 {
   111  				removeConn(conn)
   112  				conn.ClientConn.Close()
   113  				sp.idle--
   114  			}
   115  			conn = next
   116  			continue
   117  		}
   118  		//  a busy conn
   119  		if conn.streams >= p.maxStreams {
   120  			next := conn.next
   121  			removeConn(conn)
   122  			addConnAfter(conn, sp.busy)
   123  			conn = next
   124  			continue
   125  		}
   126  		//  a idle conn
   127  		if conn.streams == 0 {
   128  			sp.idle--
   129  		}
   130  		//  a good conn
   131  		conn.streams++
   132  		p.Unlock()
   133  		return conn, nil
   134  	}
   135  	p.Unlock()
   136  
   137  	//  create new conn
   138  	cc, err := grpc.Dial(addr, opts...)
   139  	if err != nil {
   140  		return nil, err
   141  	}
   142  	conn = &poolConn{cc, nil, addr, p, sp, 1, time.Now().Unix(), nil, nil, false}
   143  
   144  	//  add conn to streams pool
   145  	p.Lock()
   146  	if sp.count < p.size {
   147  		addConnAfter(conn, sp.head)
   148  	}
   149  	p.Unlock()
   150  
   151  	return conn, nil
   152  }
   153  
   154  func (p *pool) release(addr string, conn *poolConn, err error) {
   155  	p.Lock()
   156  	p, sp, created := conn.pool, conn.sp, conn.created
   157  	//  try to add conn
   158  	if !conn.in && sp.count < p.size {
   159  		addConnAfter(conn, sp.head)
   160  	}
   161  	if !conn.in {
   162  		p.Unlock()
   163  		conn.ClientConn.Close()
   164  		return
   165  	}
   166  	//  a busy conn
   167  	if conn.streams >= p.maxStreams {
   168  		removeConn(conn)
   169  		addConnAfter(conn, sp.head)
   170  	}
   171  	conn.streams--
   172  	//  if streams == 0, we can do something
   173  	if conn.streams == 0 {
   174  		//  1. it has errored
   175  		//  2. too many idle conn or
   176  		//  3. conn is too old
   177  		now := time.Now().Unix()
   178  		if err != nil || sp.idle >= p.maxIdle || now-created > p.ttl {
   179  			removeConn(conn)
   180  			p.Unlock()
   181  			conn.ClientConn.Close()
   182  			return
   183  		}
   184  		sp.idle++
   185  	}
   186  	p.Unlock()
   187  	return
   188  }
   189  
   190  func (conn *poolConn) Close() {
   191  	conn.pool.release(conn.addr, conn, conn.err)
   192  }
   193  
   194  func removeConn(conn *poolConn) {
   195  	if conn.pre != nil {
   196  		conn.pre.next = conn.next
   197  	}
   198  	if conn.next != nil {
   199  		conn.next.pre = conn.pre
   200  	}
   201  	conn.pre = nil
   202  	conn.next = nil
   203  	conn.in = false
   204  	conn.sp.count--
   205  	return
   206  }
   207  
   208  func addConnAfter(conn *poolConn, after *poolConn) {
   209  	conn.next = after.next
   210  	conn.pre = after
   211  	if after.next != nil {
   212  		after.next.pre = conn
   213  	}
   214  	after.next = conn
   215  	conn.in = true
   216  	conn.sp.count++
   217  	return
   218  }