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 }