github.com/XiaoMi/Gaea@v1.2.5/proxy/server/manager.go (about) 1 // Copyright 2019 The Gaea Authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain 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, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package server 16 17 import ( 18 "bytes" 19 "crypto/md5" 20 "fmt" 21 "net/http" 22 "sort" 23 "strconv" 24 "strings" 25 "sync" 26 "time" 27 28 "github.com/XiaoMi/Gaea/core/errors" 29 "github.com/XiaoMi/Gaea/log" 30 "github.com/XiaoMi/Gaea/log/xlog" 31 "github.com/XiaoMi/Gaea/models" 32 "github.com/XiaoMi/Gaea/mysql" 33 "github.com/XiaoMi/Gaea/parser" 34 "github.com/XiaoMi/Gaea/stats" 35 "github.com/XiaoMi/Gaea/stats/prometheus" 36 "github.com/XiaoMi/Gaea/util" 37 "github.com/XiaoMi/Gaea/util/sync2" 38 ) 39 40 // LoadAndCreateManager load namespace config, and create manager 41 func LoadAndCreateManager(cfg *models.Proxy) (*Manager, error) { 42 namespaceConfigs, err := loadAllNamespace(cfg) 43 if err != nil { 44 log.Warn("init namespace manager failed, %v", err) 45 return nil, err 46 47 } 48 49 mgr, err := CreateManager(cfg, namespaceConfigs) 50 if err != nil { 51 log.Warn("create manager error: %v", err) 52 return nil, err 53 } 54 //globalManager = mgr 55 return mgr, nil 56 } 57 58 func loadAllNamespace(cfg *models.Proxy) (map[string]*models.Namespace, error) { 59 // get names of all namespace 60 root := cfg.CoordinatorRoot 61 if cfg.ConfigType == models.ConfigFile { 62 root = cfg.FileConfigPath 63 } 64 65 client := models.NewClient(cfg.ConfigType, cfg.CoordinatorAddr, cfg.UserName, cfg.Password, root) 66 store := models.NewStore(client) 67 defer store.Close() 68 var err error 69 var names []string 70 names, err = store.ListNamespace() 71 if err != nil { 72 log.Warn("list namespace failed, err: %v", err) 73 return nil, err 74 } 75 76 // query remote namespace models in worker goroutines 77 nameC := make(chan string) 78 namespaceC := make(chan *models.Namespace) 79 var wg sync.WaitGroup 80 for i := 0; i < 10; i++ { 81 wg.Add(1) 82 go func() { 83 client := models.NewClient(cfg.ConfigType, cfg.CoordinatorAddr, cfg.UserName, cfg.Password, root) 84 store := models.NewStore(client) 85 defer store.Close() 86 defer wg.Done() 87 for name := range nameC { 88 namespace, e := store.LoadNamespace(cfg.EncryptKey, name) 89 if e != nil { 90 log.Warn("load namespace %s failed, err: %v", name, err) 91 // assign extent err out of this scope 92 err = e 93 return 94 } 95 // verify namespace config 96 e = namespace.Verify() 97 if e != nil { 98 log.Warn("verify namespace %s failed, err: %v", name, e) 99 err = e 100 return 101 } 102 namespaceC <- namespace 103 } 104 }() 105 } 106 107 // dispatch goroutine 108 go func() { 109 for _, name := range names { 110 nameC <- name 111 } 112 close(nameC) 113 wg.Wait() 114 close(namespaceC) 115 }() 116 117 // collect all namespaces 118 namespaceModels := make(map[string]*models.Namespace, 64) 119 for namespace := range namespaceC { 120 namespaceModels[namespace.Name] = namespace 121 } 122 if err != nil { 123 log.Warn("get namespace failed, err:%v", err) 124 return nil, err 125 } 126 127 return namespaceModels, nil 128 } 129 130 // Manager contains namespace manager and user manager 131 type Manager struct { 132 reloadPrepared sync2.AtomicBool 133 switchIndex util.BoolIndex 134 namespaces [2]*NamespaceManager 135 users [2]*UserManager 136 statistics *StatisticManager 137 } 138 139 // NewManager return empty Manager 140 func NewManager() *Manager { 141 return &Manager{} 142 } 143 144 // CreateManager create manager 145 func CreateManager(cfg *models.Proxy, namespaceConfigs map[string]*models.Namespace) (*Manager, error) { 146 m := NewManager() 147 148 // init statistics 149 statisticManager, err := CreateStatisticManager(cfg, m) 150 if err != nil { 151 log.Warn("init stats manager failed, %v", err) 152 return nil, err 153 } 154 m.statistics = statisticManager 155 156 current, _, _ := m.switchIndex.Get() 157 158 // init namespace 159 m.namespaces[current] = CreateNamespaceManager(namespaceConfigs) 160 161 // init user 162 user, err := CreateUserManager(namespaceConfigs) 163 if err != nil { 164 return nil, err 165 } 166 m.users[current] = user 167 168 m.startConnectPoolMetricsTask(cfg.StatsInterval) 169 return m, nil 170 } 171 172 // Close close manager 173 func (m *Manager) Close() { 174 current, _, _ := m.switchIndex.Get() 175 176 namespaces := m.namespaces[current].namespaces 177 for _, ns := range namespaces { 178 ns.Close(false) 179 } 180 181 m.statistics.Close() 182 } 183 184 // ReloadNamespacePrepare prepare commit 185 func (m *Manager) ReloadNamespacePrepare(namespaceConfig *models.Namespace) error { 186 name := namespaceConfig.Name 187 current, other, _ := m.switchIndex.Get() 188 189 // reload namespace prepare 190 currentNamespaceManager := m.namespaces[current] 191 newNamespaceManager := ShallowCopyNamespaceManager(currentNamespaceManager) 192 if err := newNamespaceManager.RebuildNamespace(namespaceConfig); err != nil { 193 log.Warn("prepare config of namespace: %s failed, err: %v", name, err) 194 return err 195 } 196 m.namespaces[other] = newNamespaceManager 197 198 // reload user prepare 199 currentUserManager := m.users[current] 200 newUserManager := CloneUserManager(currentUserManager) 201 newUserManager.RebuildNamespaceUsers(namespaceConfig) 202 m.users[other] = newUserManager 203 m.reloadPrepared.Set(true) 204 205 return nil 206 } 207 208 // ReloadNamespaceCommit commit config 209 func (m *Manager) ReloadNamespaceCommit(name string) error { 210 if !m.reloadPrepared.CompareAndSwap(true, false) { 211 err := errors.ErrNamespaceNotPrepared 212 log.Warn("commit namespace error, namespace: %s, err: %v", name, err) 213 return err 214 } 215 216 current, _, index := m.switchIndex.Get() 217 218 currentNamespace := m.namespaces[current].GetNamespace(name) 219 if currentNamespace != nil { 220 go currentNamespace.Close(true) 221 } 222 223 m.switchIndex.Set(!index) 224 225 return nil 226 } 227 228 // DeleteNamespace delete namespace 229 func (m *Manager) DeleteNamespace(name string) error { 230 current, other, index := m.switchIndex.Get() 231 232 // idempotent delete 233 currentNamespace := m.namespaces[current].GetNamespace(name) 234 if currentNamespace == nil { 235 return nil 236 } 237 238 // delete namespace of other 239 currentNamespaceManager := m.namespaces[current] 240 newNamespaceManager := ShallowCopyNamespaceManager(currentNamespaceManager) 241 newNamespaceManager.DeleteNamespace(name) 242 m.namespaces[other] = newNamespaceManager 243 244 // delete users of other 245 currentUserManager := m.users[current] 246 newUserManager := CloneUserManager(currentUserManager) 247 newUserManager.ClearNamespaceUsers(name) 248 m.users[other] = newUserManager 249 250 // switch namespace manager 251 m.switchIndex.Set(!index) 252 253 // delay recycle resources of current 254 go currentNamespace.Close(true) 255 256 return nil 257 } 258 259 // GetNamespace return specific namespace 260 func (m *Manager) GetNamespace(name string) *Namespace { 261 current, _, _ := m.switchIndex.Get() 262 return m.namespaces[current].GetNamespace(name) 263 } 264 265 // CheckUser check if user in users 266 func (m *Manager) CheckUser(user string) bool { 267 current, _, _ := m.switchIndex.Get() 268 return m.users[current].CheckUser(user) 269 } 270 271 // CheckPassword check if right password with specific user 272 func (m *Manager) CheckPassword(user string, salt, auth []byte) (bool, string) { 273 current, _, _ := m.switchIndex.Get() 274 return m.users[current].CheckPassword(user, salt, auth) 275 } 276 277 // CheckPassword check if right password with specific user 278 func (m *Manager) CheckSha2Password(user string, salt, auth []byte) (bool, string) { 279 current, _, _ := m.switchIndex.Get() 280 return m.users[current].CheckSha2Password(user, salt, auth) 281 } 282 283 // GetStatisticManager return proxy status to record status 284 func (m *Manager) GetStatisticManager() *StatisticManager { 285 return m.statistics 286 } 287 288 // GetNamespaceByUser return namespace by user 289 func (m *Manager) GetNamespaceByUser(userName, password string) string { 290 current, _, _ := m.switchIndex.Get() 291 return m.users[current].GetNamespaceByUser(userName, password) 292 } 293 294 // ConfigFingerprint return config fingerprint 295 func (m *Manager) ConfigFingerprint() string { 296 current, _, _ := m.switchIndex.Get() 297 return m.namespaces[current].ConfigFingerprint() 298 } 299 300 // RecordSessionSQLMetrics record session SQL metrics, like response time, error 301 func (m *Manager) RecordSessionSQLMetrics(reqCtx *util.RequestContext, se *SessionExecutor, sql string, startTime time.Time, err error) { 302 trimmedSql := strings.ReplaceAll(sql, "\n", " ") 303 namespace := se.namespace 304 ns := m.GetNamespace(namespace) 305 if ns == nil { 306 log.Warn("record session SQL metrics error, namespace: %s, sql: %s, err: %s", namespace, trimmedSql, "namespace not found") 307 return 308 } 309 310 var operation string 311 if stmtType, ok := reqCtx.Get(util.StmtType).(int); ok { 312 operation = parser.StmtType(stmtType) 313 } else { 314 fingerprint := mysql.GetFingerprint(sql) 315 operation = mysql.GetFingerprintOperation(fingerprint) 316 } 317 318 // record sql timing 319 m.statistics.recordSessionSQLTiming(namespace, operation, startTime) 320 321 // record slow sql 322 duration := time.Since(startTime).Nanoseconds() / int64(time.Millisecond) 323 if duration > ns.getSessionSlowSQLTime() || ns.getSessionSlowSQLTime() == 0 { 324 log.Warn("session slow SQL, namespace: %s, sql: %s, cost: %d ms", namespace, trimmedSql, duration) 325 fingerprint := mysql.GetFingerprint(sql) 326 md5 := mysql.GetMd5(fingerprint) 327 ns.SetSlowSQLFingerprint(md5, fingerprint) 328 m.statistics.recordSessionSlowSQLFingerprint(namespace, md5) 329 } 330 331 // record error sql 332 if err != nil { 333 log.Warn("session error SQL, namespace: %s, sql: %s, cost: %d ms, err: %v", namespace, trimmedSql, duration, err) 334 fingerprint := mysql.GetFingerprint(sql) 335 md5 := mysql.GetMd5(fingerprint) 336 ns.SetErrorSQLFingerprint(md5, fingerprint) 337 m.statistics.recordSessionErrorSQLFingerprint(namespace, operation, md5) 338 } 339 340 if OpenProcessGeneralQueryLog() && ns.openGeneralLog { 341 m.statistics.generalLogger.Notice("client: %s, namespace: %s, db: %s, user: %s, cmd: %s, sql: %s, cost: %d ms, succ: %t", 342 se.clientAddr, namespace, se.db, se.user, operation, trimmedSql, duration, err == nil) 343 } 344 } 345 346 // RecordBackendSQLMetrics record backend SQL metrics, like response time, error 347 func (m *Manager) RecordBackendSQLMetrics(reqCtx *util.RequestContext, namespace string, sql, backendAddr string, startTime time.Time, err error) { 348 trimmedSql := strings.ReplaceAll(sql, "\n", " ") 349 ns := m.GetNamespace(namespace) 350 if ns == nil { 351 log.Warn("record backend SQL metrics error, namespace: %s, backend addr: %s, sql: %s, err: %s", namespace, backendAddr, trimmedSql, "namespace not found") 352 return 353 } 354 355 var operation string 356 if stmtType, ok := reqCtx.Get(util.StmtType).(int); ok { 357 operation = parser.StmtType(stmtType) 358 } else { 359 fingerprint := mysql.GetFingerprint(sql) 360 operation = mysql.GetFingerprintOperation(fingerprint) 361 } 362 363 // record sql timing 364 m.statistics.recordBackendSQLTiming(namespace, operation, startTime) 365 366 // record slow sql 367 duration := time.Since(startTime).Nanoseconds() / int64(time.Millisecond) 368 if m.statistics.isBackendSlowSQL(startTime) { 369 log.Warn("backend slow SQL, namespace: %s, addr: %s, sql: %s, cost: %d ms", namespace, backendAddr, trimmedSql, duration) 370 fingerprint := mysql.GetFingerprint(sql) 371 md5 := mysql.GetMd5(fingerprint) 372 ns.SetBackendSlowSQLFingerprint(md5, fingerprint) 373 m.statistics.recordBackendSlowSQLFingerprint(namespace, md5) 374 } 375 376 // record error sql 377 if err != nil { 378 log.Warn("backend error SQL, namespace: %s, addr: %s, sql: %s, cost %d ms, err: %v", namespace, backendAddr, trimmedSql, duration, err) 379 fingerprint := mysql.GetFingerprint(sql) 380 md5 := mysql.GetMd5(fingerprint) 381 ns.SetBackendErrorSQLFingerprint(md5, fingerprint) 382 m.statistics.recordBackendErrorSQLFingerprint(namespace, operation, md5) 383 } 384 } 385 386 func (m *Manager) startConnectPoolMetricsTask(interval int) { 387 if interval <= 0 { 388 interval = 10 389 } 390 391 go func() { 392 t := time.NewTicker(time.Duration(interval) * time.Second) 393 for { 394 select { 395 case <-m.GetStatisticManager().closeChan: 396 return 397 case <-t.C: 398 current, _, _ := m.switchIndex.Get() 399 for nameSpaceName, _ := range m.namespaces[current].namespaces { 400 m.recordBackendConnectPoolMetrics(nameSpaceName) 401 } 402 } 403 } 404 }() 405 } 406 407 func (m *Manager) recordBackendConnectPoolMetrics(namespace string) { 408 ns := m.GetNamespace(namespace) 409 if ns == nil { 410 log.Warn("record backend connect pool metrics err, namespace: %s", namespace) 411 return 412 } 413 414 for sliceName, slice := range ns.slices { 415 m.statistics.recordConnectPoolInuseCount(namespace, sliceName, slice.Master.Addr(), slice.Master.InUse()) 416 m.statistics.recordConnectPoolIdleCount(namespace, sliceName, slice.Master.Addr(), slice.Master.Available()) 417 m.statistics.recordConnectPoolWaitCount(namespace, sliceName, slice.Master.Addr(), slice.Master.WaitCount()) 418 for _, slave := range slice.Slave { 419 m.statistics.recordConnectPoolInuseCount(namespace, sliceName, slave.Addr(), slave.InUse()) 420 m.statistics.recordConnectPoolIdleCount(namespace, sliceName, slave.Addr(), slave.Available()) 421 m.statistics.recordConnectPoolWaitCount(namespace, sliceName, slave.Addr(), slave.WaitCount()) 422 } 423 for _, statisticSlave := range slice.StatisticSlave { 424 m.statistics.recordConnectPoolInuseCount(namespace, sliceName, statisticSlave.Addr(), statisticSlave.InUse()) 425 m.statistics.recordConnectPoolIdleCount(namespace, sliceName, statisticSlave.Addr(), statisticSlave.Available()) 426 m.statistics.recordConnectPoolWaitCount(namespace, sliceName, statisticSlave.Addr(), statisticSlave.WaitCount()) 427 } 428 } 429 } 430 431 // NamespaceManager is the manager that holds all namespaces 432 type NamespaceManager struct { 433 namespaces map[string]*Namespace 434 } 435 436 // NewNamespaceManager constructor of NamespaceManager 437 func NewNamespaceManager() *NamespaceManager { 438 return &NamespaceManager{ 439 namespaces: make(map[string]*Namespace, 64), 440 } 441 } 442 443 // CreateNamespaceManager create NamespaceManager 444 func CreateNamespaceManager(namespaceConfigs map[string]*models.Namespace) *NamespaceManager { 445 nsMgr := NewNamespaceManager() 446 for _, config := range namespaceConfigs { 447 namespace, err := NewNamespace(config) 448 if err != nil { 449 log.Warn("create namespace %s failed, err: %v", config.Name, err) 450 continue 451 } 452 nsMgr.namespaces[namespace.name] = namespace 453 } 454 return nsMgr 455 } 456 457 // ShallowCopyNamespaceManager copy NamespaceManager 458 func ShallowCopyNamespaceManager(nsMgr *NamespaceManager) *NamespaceManager { 459 newNsMgr := NewNamespaceManager() 460 for k, v := range nsMgr.namespaces { 461 newNsMgr.namespaces[k] = v 462 } 463 return newNsMgr 464 } 465 466 // RebuildNamespace rebuild namespace 467 func (n *NamespaceManager) RebuildNamespace(config *models.Namespace) error { 468 namespace, err := NewNamespace(config) 469 if err != nil { 470 log.Warn("create namespace %s failed, err: %v", config.Name, err) 471 return err 472 } 473 n.namespaces[config.Name] = namespace 474 return nil 475 } 476 477 // DeleteNamespace delete namespace 478 func (n *NamespaceManager) DeleteNamespace(ns string) { 479 delete(n.namespaces, ns) 480 } 481 482 // GetNamespace get namespace in NamespaceManager 483 func (n *NamespaceManager) GetNamespace(namespace string) *Namespace { 484 return n.namespaces[namespace] 485 } 486 487 // GetNamespaces return all namespaces in NamespaceManager 488 func (n *NamespaceManager) GetNamespaces() map[string]*Namespace { 489 return n.namespaces 490 } 491 492 // ConfigFingerprint return config fingerprint 493 func (n *NamespaceManager) ConfigFingerprint() string { 494 sortedKeys := make([]string, 0) 495 for k := range n.GetNamespaces() { 496 sortedKeys = append(sortedKeys, k) 497 } 498 499 sort.Strings(sortedKeys) 500 501 h := md5.New() 502 for _, k := range sortedKeys { 503 h.Write(n.GetNamespace(k).DumpToJSON()) 504 } 505 return fmt.Sprintf("%x", h.Sum(nil)) 506 } 507 508 // UserManager means user for auth 509 // username+password是全局唯一的, 而username可以对应多个namespace 510 type UserManager struct { 511 users map[string][]string // key: user name, value: user password, same user may have different password, so array of passwords is needed 512 userNamespaces map[string]string // key: UserName+Password, value: name of namespace 513 } 514 515 // NewUserManager constructor of UserManager 516 func NewUserManager() *UserManager { 517 return &UserManager{ 518 users: make(map[string][]string, 64), 519 userNamespaces: make(map[string]string, 64), 520 } 521 } 522 523 // CreateUserManager create UserManager 524 func CreateUserManager(namespaceConfigs map[string]*models.Namespace) (*UserManager, error) { 525 user := NewUserManager() 526 for _, ns := range namespaceConfigs { 527 user.addNamespaceUsers(ns) 528 } 529 return user, nil 530 } 531 532 // CloneUserManager close UserManager 533 func CloneUserManager(user *UserManager) *UserManager { 534 ret := NewUserManager() 535 // copy 536 for k, v := range user.userNamespaces { 537 ret.userNamespaces[k] = v 538 } 539 for k, v := range user.users { 540 users := make([]string, len(v)) 541 copy(users, v) 542 ret.users[k] = users 543 } 544 545 return ret 546 } 547 548 // RebuildNamespaceUsers rebuild users in namespace 549 func (u *UserManager) RebuildNamespaceUsers(namespace *models.Namespace) { 550 u.ClearNamespaceUsers(namespace.Name) 551 u.addNamespaceUsers(namespace) 552 } 553 554 // ClearNamespaceUsers clear users in namespace 555 func (u *UserManager) ClearNamespaceUsers(namespace string) { 556 for key, ns := range u.userNamespaces { 557 if ns == namespace { 558 delete(u.userNamespaces, key) 559 560 // delete user password in users 561 username, password := getUserAndPasswordFromKey(key) 562 var s []string 563 for i := range u.users[username] { 564 if u.users[username][i] == password { 565 s = append(u.users[username][:i], u.users[username][i+1:]...) 566 } 567 } 568 u.users[username] = s 569 } 570 } 571 } 572 573 func (u *UserManager) addNamespaceUsers(namespace *models.Namespace) { 574 for _, user := range namespace.Users { 575 key := getUserKey(user.UserName, user.Password) 576 u.userNamespaces[key] = namespace.Name 577 u.users[user.UserName] = append(u.users[user.UserName], user.Password) 578 } 579 } 580 581 // CheckUser check if user in users 582 func (u *UserManager) CheckUser(user string) bool { 583 if _, ok := u.users[user]; ok { 584 return true 585 } 586 return false 587 } 588 589 // CheckPassword check if right password with specific user 590 func (u *UserManager) CheckPassword(user string, salt, auth []byte) (bool, string) { 591 for _, password := range u.users[user] { 592 checkAuth := mysql.CalcPassword(salt, []byte(password)) 593 if bytes.Equal(auth, checkAuth) { 594 return true, password 595 } 596 } 597 return false, "" 598 } 599 600 // CheckPassword check if right password with specific user 601 func (u *UserManager) CheckSha2Password(user string, salt, auth []byte) (bool, string) { 602 for _, password := range u.users[user] { 603 checkAuth := mysql.CalcCachingSha2Password(salt, password) 604 if bytes.Equal(auth, checkAuth) { 605 return true, password 606 } 607 } 608 return false, "" 609 } 610 611 // GetNamespaceByUser return namespace by user 612 func (u *UserManager) GetNamespaceByUser(userName, password string) string { 613 key := getUserKey(userName, password) 614 if name, ok := u.userNamespaces[key]; ok { 615 return name 616 } 617 return "" 618 } 619 620 func getUserKey(username, password string) string { 621 return username + ":" + password 622 } 623 624 func getUserAndPasswordFromKey(key string) (username string, password string) { 625 strs := strings.Split(key, ":") 626 return strs[0], strs[1] 627 } 628 629 const ( 630 statsLabelCluster = "Cluster" 631 statsLabelOperation = "Operation" 632 statsLabelNamespace = "Namespace" 633 statsLabelFingerprint = "Fingerprint" 634 statsLabelFlowDirection = "Flowdirection" 635 statsLabelSlice = "Slice" 636 statsLabelIPAddr = "IPAddr" 637 ) 638 639 // StatisticManager statistics manager 640 type StatisticManager struct { 641 manager *Manager 642 clusterName string 643 644 statsType string // 监控后端类型 645 handlers map[string]http.Handler 646 generalLogger log.Logger 647 648 sqlTimings *stats.MultiTimings // SQL耗时统计 649 sqlFingerprintSlowCounts *stats.CountersWithMultiLabels // 慢SQL指纹数量统计 650 sqlErrorCounts *stats.CountersWithMultiLabels // SQL错误数统计 651 sqlFingerprintErrorCounts *stats.CountersWithMultiLabels // SQL指纹错误数统计 652 sqlForbidenCounts *stats.CountersWithMultiLabels // SQL黑名单请求统计 653 flowCounts *stats.CountersWithMultiLabels // 业务流量统计 654 sessionCounts *stats.GaugesWithMultiLabels // 前端会话数统计 655 656 backendSQLTimings *stats.MultiTimings // 后端SQL耗时统计 657 backendSQLFingerprintSlowCounts *stats.CountersWithMultiLabels // 后端慢SQL指纹数量统计 658 backendSQLErrorCounts *stats.CountersWithMultiLabels // 后端SQL错误数统计 659 backendSQLFingerprintErrorCounts *stats.CountersWithMultiLabels // 后端SQL指纹错误数统计 660 backendConnectPoolIdleCounts *stats.GaugesWithMultiLabels //后端空闲连接数统计 661 backendConnectPoolInUseCounts *stats.GaugesWithMultiLabels //后端正在使用连接数统计 662 backendConnectPoolWaitCounts *stats.GaugesWithMultiLabels //后端等待队列统计 663 664 slowSQLTime int64 665 closeChan chan bool 666 } 667 668 // NewStatisticManager return empty StatisticManager 669 func NewStatisticManager() *StatisticManager { 670 return &StatisticManager{} 671 } 672 673 // CreateStatisticManager create StatisticManager 674 func CreateStatisticManager(cfg *models.Proxy, manager *Manager) (*StatisticManager, error) { 675 mgr := NewStatisticManager() 676 mgr.manager = manager 677 mgr.clusterName = cfg.Cluster 678 679 var err error 680 if err = mgr.Init(cfg); err != nil { 681 return nil, err 682 } 683 if mgr.generalLogger, err = initGeneralLogger(cfg); err != nil { 684 return nil, err 685 } 686 return mgr, nil 687 } 688 689 type proxyStatsConfig struct { 690 Service string 691 StatsEnabled bool 692 } 693 694 func initGeneralLogger(cfg *models.Proxy) (log.Logger, error) { 695 c := make(map[string]string, 5) 696 c["path"] = cfg.LogPath 697 c["filename"] = cfg.LogFileName + "_sql" 698 c["level"] = cfg.LogLevel 699 c["service"] = cfg.Service 700 c["runtime"] = "false" 701 return xlog.CreateLogManager(cfg.LogOutput, c) 702 } 703 704 func parseProxyStatsConfig(cfg *models.Proxy) (*proxyStatsConfig, error) { 705 enabled, err := strconv.ParseBool(cfg.StatsEnabled) 706 if err != nil { 707 return nil, err 708 } 709 710 statsConfig := &proxyStatsConfig{ 711 Service: cfg.Service, 712 StatsEnabled: enabled, 713 } 714 return statsConfig, nil 715 } 716 717 // Init init StatisticManager 718 func (s *StatisticManager) Init(cfg *models.Proxy) error { 719 s.closeChan = make(chan bool, 0) 720 s.handlers = make(map[string]http.Handler) 721 s.slowSQLTime = cfg.SlowSQLTime 722 statsCfg, err := parseProxyStatsConfig(cfg) 723 if err != nil { 724 return err 725 } 726 727 if err := s.initBackend(statsCfg); err != nil { 728 return err 729 } 730 731 s.sqlTimings = stats.NewMultiTimings("SqlTimings", 732 "gaea proxy sql sqlTimings", []string{statsLabelCluster, statsLabelNamespace, statsLabelOperation}) 733 s.sqlFingerprintSlowCounts = stats.NewCountersWithMultiLabels("SqlFingerprintSlowCounts", 734 "gaea proxy sql fingerprint slow counts", []string{statsLabelCluster, statsLabelNamespace, statsLabelFingerprint}) 735 s.sqlErrorCounts = stats.NewCountersWithMultiLabels("SqlErrorCounts", 736 "gaea proxy sql error counts per error type", []string{statsLabelCluster, statsLabelNamespace, statsLabelOperation}) 737 s.sqlFingerprintErrorCounts = stats.NewCountersWithMultiLabels("SqlFingerprintErrorCounts", 738 "gaea proxy sql fingerprint error counts", []string{statsLabelCluster, statsLabelNamespace, statsLabelFingerprint}) 739 s.sqlForbidenCounts = stats.NewCountersWithMultiLabels("SqlForbiddenCounts", 740 "gaea proxy sql error counts per error type", []string{statsLabelCluster, statsLabelNamespace, statsLabelFingerprint}) 741 s.flowCounts = stats.NewCountersWithMultiLabels("FlowCounts", 742 "gaea proxy flow counts", []string{statsLabelCluster, statsLabelNamespace, statsLabelFlowDirection}) 743 s.sessionCounts = stats.NewGaugesWithMultiLabels("SessionCounts", 744 "gaea proxy session counts", []string{statsLabelCluster, statsLabelNamespace}) 745 746 s.backendSQLTimings = stats.NewMultiTimings("BackendSqlTimings", 747 "gaea proxy backend sql sqlTimings", []string{statsLabelCluster, statsLabelNamespace, statsLabelOperation}) 748 s.backendSQLFingerprintSlowCounts = stats.NewCountersWithMultiLabels("BackendSqlFingerprintSlowCounts", 749 "gaea proxy backend sql fingerprint slow counts", []string{statsLabelCluster, statsLabelNamespace, statsLabelFingerprint}) 750 s.backendSQLErrorCounts = stats.NewCountersWithMultiLabels("BackendSqlErrorCounts", 751 "gaea proxy backend sql error counts per error type", []string{statsLabelCluster, statsLabelNamespace, statsLabelOperation}) 752 s.backendSQLFingerprintErrorCounts = stats.NewCountersWithMultiLabels("BackendSqlFingerprintErrorCounts", 753 "gaea proxy backend sql fingerprint error counts", []string{statsLabelCluster, statsLabelNamespace, statsLabelFingerprint}) 754 s.backendConnectPoolIdleCounts = stats.NewGaugesWithMultiLabels("backendConnectPoolIdleCounts", 755 "gaea proxy backend idle connect counts", []string{statsLabelCluster, statsLabelNamespace, statsLabelSlice, statsLabelIPAddr}) 756 s.backendConnectPoolInUseCounts = stats.NewGaugesWithMultiLabels("backendConnectPoolInUseCounts", 757 "gaea proxy backend in-use connect counts", []string{statsLabelCluster, statsLabelNamespace, statsLabelSlice, statsLabelIPAddr}) 758 s.backendConnectPoolWaitCounts = stats.NewGaugesWithMultiLabels("backendConnectPoolWaitCounts", 759 "gaea proxy backend wait connect counts", []string{statsLabelCluster, statsLabelNamespace, statsLabelSlice, statsLabelIPAddr}) 760 761 s.startClearTask() 762 return nil 763 } 764 765 // Close close proxy stats 766 func (s *StatisticManager) Close() { 767 close(s.closeChan) 768 } 769 770 // GetHandlers return specific handler of stats 771 func (s *StatisticManager) GetHandlers() map[string]http.Handler { 772 return s.handlers 773 } 774 775 func (s *StatisticManager) initBackend(cfg *proxyStatsConfig) error { 776 prometheus.Init(cfg.Service) 777 s.handlers = prometheus.GetHandlers() 778 return nil 779 } 780 781 // clear data to prevent 782 func (s *StatisticManager) startClearTask() { 783 go func() { 784 t := time.NewTicker(time.Hour) 785 for { 786 select { 787 case <-s.closeChan: 788 return 789 case <-t.C: 790 s.clearLargeCounters() 791 } 792 } 793 }() 794 } 795 796 func (s *StatisticManager) clearLargeCounters() { 797 s.sqlErrorCounts.ResetAll() 798 s.sqlFingerprintSlowCounts.ResetAll() 799 s.sqlFingerprintErrorCounts.ResetAll() 800 801 s.backendSQLErrorCounts.ResetAll() 802 s.backendSQLFingerprintSlowCounts.ResetAll() 803 s.backendSQLFingerprintErrorCounts.ResetAll() 804 } 805 806 func (s *StatisticManager) recordSessionSlowSQLFingerprint(namespace string, md5 string) { 807 fingerprintStatsKey := []string{s.clusterName, namespace, md5} 808 s.sqlFingerprintSlowCounts.Add(fingerprintStatsKey, 1) 809 } 810 811 func (s *StatisticManager) recordSessionErrorSQLFingerprint(namespace string, operation string, md5 string) { 812 fingerprintStatsKey := []string{s.clusterName, namespace, md5} 813 operationStatsKey := []string{s.clusterName, namespace, operation} 814 s.sqlErrorCounts.Add(operationStatsKey, 1) 815 s.sqlFingerprintErrorCounts.Add(fingerprintStatsKey, 1) 816 } 817 818 func (s *StatisticManager) recordSessionSQLTiming(namespace string, operation string, startTime time.Time) { 819 operationStatsKey := []string{s.clusterName, namespace, operation} 820 s.sqlTimings.Record(operationStatsKey, startTime) 821 } 822 823 // millisecond duration 824 func (s *StatisticManager) isBackendSlowSQL(startTime time.Time) bool { 825 duration := time.Since(startTime).Nanoseconds() / int64(time.Millisecond) 826 return duration > s.slowSQLTime || s.slowSQLTime == 0 827 } 828 829 func (s *StatisticManager) recordBackendSlowSQLFingerprint(namespace string, md5 string) { 830 fingerprintStatsKey := []string{s.clusterName, namespace, md5} 831 s.backendSQLFingerprintSlowCounts.Add(fingerprintStatsKey, 1) 832 } 833 834 func (s *StatisticManager) recordBackendErrorSQLFingerprint(namespace string, operation string, md5 string) { 835 fingerprintStatsKey := []string{s.clusterName, namespace, md5} 836 operationStatsKey := []string{s.clusterName, namespace, operation} 837 s.backendSQLErrorCounts.Add(operationStatsKey, 1) 838 s.backendSQLFingerprintErrorCounts.Add(fingerprintStatsKey, 1) 839 } 840 841 func (s *StatisticManager) recordBackendSQLTiming(namespace string, operation string, startTime time.Time) { 842 operationStatsKey := []string{s.clusterName, namespace, operation} 843 s.backendSQLTimings.Record(operationStatsKey, startTime) 844 } 845 846 // RecordSQLForbidden record forbidden sql 847 func (s *StatisticManager) RecordSQLForbidden(fingerprint, namespace string) { 848 md5 := mysql.GetMd5(fingerprint) 849 s.sqlForbidenCounts.Add([]string{s.clusterName, namespace, md5}, 1) 850 } 851 852 // IncrSessionCount incr session count 853 func (s *StatisticManager) IncrSessionCount(namespace string) { 854 statsKey := []string{s.clusterName, namespace} 855 s.sessionCounts.Add(statsKey, 1) 856 } 857 858 // DescSessionCount decr session count 859 func (s *StatisticManager) DescSessionCount(namespace string) { 860 statsKey := []string{s.clusterName, namespace} 861 s.sessionCounts.Add(statsKey, -1) 862 } 863 864 // AddReadFlowCount add read flow count 865 func (s *StatisticManager) AddReadFlowCount(namespace string, byteCount int) { 866 statsKey := []string{s.clusterName, namespace, "read"} 867 s.flowCounts.Add(statsKey, int64(byteCount)) 868 } 869 870 // AddWriteFlowCount add write flow count 871 func (s *StatisticManager) AddWriteFlowCount(namespace string, byteCount int) { 872 statsKey := []string{s.clusterName, namespace, "write"} 873 s.flowCounts.Add(statsKey, int64(byteCount)) 874 } 875 876 //record idle connect count 877 func (s *StatisticManager) recordConnectPoolIdleCount(namespace string, slice string, addr string, count int64) { 878 statsKey := []string{s.clusterName, namespace, slice, addr} 879 s.backendConnectPoolIdleCounts.Set(statsKey, count) 880 } 881 882 //record in-use connect count 883 func (s *StatisticManager) recordConnectPoolInuseCount(namespace string, slice string, addr string, count int64) { 884 statsKey := []string{s.clusterName, namespace, slice, addr} 885 s.backendConnectPoolInUseCounts.Set(statsKey, count) 886 } 887 888 //record wait queue length 889 func (s *StatisticManager) recordConnectPoolWaitCount(namespace string, slice string, addr string, count int64) { 890 statsKey := []string{s.clusterName, namespace, slice, addr} 891 s.backendConnectPoolWaitCounts.Set(statsKey, count) 892 }