github.com/XiaoMi/Gaea@v1.2.5/backend/slice.go (about)

     1  // Copyright 2016 The kingshard Authors. All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License"): you may
     4  // not use this file except in compliance with the License. You may obtain
     5  // a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    11  // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
    12  // License for the specific language governing permissions and limitations
    13  // under the License.
    14  
    15  // Copyright 2019 The Gaea Authors. All Rights Reserved.
    16  //
    17  // Licensed under the Apache License, Version 2.0 (the "License");
    18  // you may not use this file except in compliance with the License.
    19  // You may obtain a copy of the License at
    20  //
    21  //     http://www.apache.org/licenses/LICENSE-2.0
    22  //
    23  // Unless required by applicable law or agreed to in writing, software
    24  // distributed under the License is distributed on an "AS IS" BASIS,
    25  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    26  // See the License for the specific language governing permissions and
    27  // limitations under the License.
    28  
    29  package backend
    30  
    31  import (
    32  	"context"
    33  	"strconv"
    34  	"strings"
    35  	"sync"
    36  
    37  	"github.com/XiaoMi/Gaea/core/errors"
    38  	"github.com/XiaoMi/Gaea/log"
    39  	"github.com/XiaoMi/Gaea/models"
    40  	"github.com/XiaoMi/Gaea/mysql"
    41  	"github.com/XiaoMi/Gaea/util"
    42  )
    43  
    44  const (
    45  	weightSplit = "@"
    46  
    47  	// DefaultSlice means default slice for namespace
    48  	DefaultSlice = "slice-0"
    49  )
    50  
    51  // Slice means one slice of the mysql cluster
    52  type Slice struct {
    53  	Cfg models.Slice
    54  	sync.RWMutex
    55  
    56  	Master ConnectionPool
    57  
    58  	Slave         []ConnectionPool
    59  	slaveBalancer *balancer
    60  
    61  	StatisticSlave         []ConnectionPool
    62  	statisticSlaveBalancer *balancer
    63  
    64  	charset     string
    65  	collationID mysql.CollationID
    66  }
    67  
    68  // GetSliceName return name of slice
    69  func (s *Slice) GetSliceName() string {
    70  	return s.Cfg.Name
    71  }
    72  
    73  // GetConn get backend connection from different node based on fromSlave and userType
    74  func (s *Slice) GetConn(fromSlave bool, userType int) (pc PooledConnect, err error) {
    75  	if fromSlave {
    76  		if userType == models.StatisticUser {
    77  			pc, err = s.GetStatisticSlaveConn()
    78  			if err != nil {
    79  				return nil, err
    80  			}
    81  		} else {
    82  			pc, err = s.GetSlaveConn()
    83  			if err != nil {
    84  				log.Warn("get connection from slave failed, try to get from master, error: %s", err.Error())
    85  				pc, err = s.GetMasterConn()
    86  			}
    87  		}
    88  	} else {
    89  		pc, err = s.GetMasterConn()
    90  	}
    91  	if err != nil {
    92  		log.Warn("get connection from backend failed, error: %s", err.Error())
    93  		return
    94  	}
    95  	return
    96  }
    97  
    98  func (s *Slice) GetDirectConn(addr string) (*DirectConnection, error) {
    99  	return NewDirectConnection(addr, s.Cfg.UserName, s.Cfg.Password, "", s.charset, s.collationID)
   100  }
   101  
   102  // GetMasterConn return a connection in master pool
   103  func (s *Slice) GetMasterConn() (PooledConnect, error) {
   104  	ctx := context.TODO()
   105  	return s.Master.Get(ctx)
   106  }
   107  
   108  // GetSlaveConn return a connection in slave pool
   109  func (s *Slice) GetSlaveConn() (PooledConnect, error) {
   110  	if len(s.Slave) == 0 {
   111  		return nil, errors.ErrNoDatabase
   112  	}
   113  
   114  	s.Lock()
   115  	index, err := s.slaveBalancer.next()
   116  	s.Unlock()
   117  	if err != nil {
   118  		return nil, err
   119  	}
   120  	ctx := context.TODO()
   121  	return s.Slave[index].Get(ctx)
   122  }
   123  
   124  // GetStatisticSlaveConn return a connection in statistic slave pool
   125  func (s *Slice) GetStatisticSlaveConn() (PooledConnect, error) {
   126  	if len(s.StatisticSlave) == 0 {
   127  		return nil, errors.ErrNoDatabase
   128  	}
   129  
   130  	s.Lock()
   131  	index, err := s.statisticSlaveBalancer.next()
   132  	s.Unlock()
   133  	if err != nil {
   134  		return nil, err
   135  	}
   136  	ctx := context.TODO()
   137  	return s.StatisticSlave[index].Get(ctx)
   138  }
   139  
   140  // Close close the pool in slice
   141  func (s *Slice) Close() error {
   142  	s.Lock()
   143  	defer s.Unlock()
   144  	// close master
   145  	s.Master.Close()
   146  
   147  	// close slaves
   148  	for i := range s.Slave {
   149  		s.Slave[i].Close()
   150  	}
   151  
   152  	// close statistic slaves
   153  	for i := range s.StatisticSlave {
   154  		s.StatisticSlave[i].Close()
   155  	}
   156  
   157  	return nil
   158  }
   159  
   160  // ParseMaster create master connection pool
   161  func (s *Slice) ParseMaster(masterStr string) error {
   162  	if len(masterStr) == 0 {
   163  		return errors.ErrNoMasterDB
   164  	}
   165  	idleTimeout, err := util.Int2TimeDuration(s.Cfg.IdleTimeout)
   166  	if err != nil {
   167  		return err
   168  	}
   169  	s.Master = NewConnectionPool(masterStr, s.Cfg.UserName, s.Cfg.Password, "", s.Cfg.Capacity, s.Cfg.MaxCapacity, idleTimeout, s.charset, s.collationID)
   170  	s.Master.Open()
   171  	return nil
   172  }
   173  
   174  // ParseSlave create connection pool of slaves
   175  // (127.0.0.1:3306@2,192.168.0.12:3306@3)
   176  func (s *Slice) ParseSlave(slaves []string) error {
   177  	if len(slaves) == 0 {
   178  		return nil
   179  	}
   180  
   181  	var err error
   182  	var weight int
   183  
   184  	count := len(slaves)
   185  	s.Slave = make([]ConnectionPool, 0, count)
   186  	slaveWeights := make([]int, 0, count)
   187  
   188  	//parse addr and weight
   189  	for i := 0; i < count; i++ {
   190  		addrAndWeight := strings.Split(slaves[i], weightSplit)
   191  		if len(addrAndWeight) == 2 {
   192  			weight, err = strconv.Atoi(addrAndWeight[1])
   193  			if err != nil {
   194  				return err
   195  			}
   196  		} else {
   197  			weight = 1
   198  		}
   199  		slaveWeights = append(slaveWeights, weight)
   200  		idleTimeout, err := util.Int2TimeDuration(s.Cfg.IdleTimeout)
   201  		if err != nil {
   202  			return err
   203  		}
   204  		cp := NewConnectionPool(addrAndWeight[0], s.Cfg.UserName, s.Cfg.Password, "", s.Cfg.Capacity, s.Cfg.MaxCapacity, idleTimeout, s.charset, s.collationID)
   205  		cp.Open()
   206  		s.Slave = append(s.Slave, cp)
   207  	}
   208  	s.slaveBalancer = newBalancer(slaveWeights, len(s.Slave))
   209  	return nil
   210  }
   211  
   212  // ParseStatisticSlave create connection pool of statistic slaves
   213  // slaveStr(127.0.0.1:3306@2,192.168.0.12:3306@3)
   214  func (s *Slice) ParseStatisticSlave(statisticSlaves []string) error {
   215  	if len(statisticSlaves) == 0 {
   216  		return nil
   217  	}
   218  
   219  	var err error
   220  	var weight int
   221  
   222  	count := len(statisticSlaves)
   223  	s.StatisticSlave = make([]ConnectionPool, 0, count)
   224  	statisticSlaveWeights := make([]int, 0, count)
   225  
   226  	//parse addr and weight
   227  	for i := 0; i < count; i++ {
   228  		addrAndWeight := strings.Split(statisticSlaves[i], weightSplit)
   229  		if len(addrAndWeight) == 2 {
   230  			weight, err = strconv.Atoi(addrAndWeight[1])
   231  			if err != nil {
   232  				return err
   233  			}
   234  		} else {
   235  			weight = 1
   236  		}
   237  		statisticSlaveWeights = append(statisticSlaveWeights, weight)
   238  		idleTimeout, err := util.Int2TimeDuration(s.Cfg.IdleTimeout)
   239  		if err != nil {
   240  			return err
   241  		}
   242  		cp := NewConnectionPool(addrAndWeight[0], s.Cfg.UserName, s.Cfg.Password, "", s.Cfg.Capacity, s.Cfg.MaxCapacity, idleTimeout, s.charset, s.collationID)
   243  		cp.Open()
   244  		s.StatisticSlave = append(s.StatisticSlave, cp)
   245  	}
   246  	s.statisticSlaveBalancer = newBalancer(statisticSlaveWeights, len(s.StatisticSlave))
   247  	return nil
   248  }
   249  
   250  // SetCharsetInfo set charset
   251  func (s *Slice) SetCharsetInfo(charset string, collationID mysql.CollationID) {
   252  	s.charset = charset
   253  	s.collationID = collationID
   254  }