gitee.com/curryzheng/dm@v0.0.1/z.go (about) 1 /* 2 * Copyright (c) 2000-2018, 达梦数据库有限公司. 3 * All rights reserved. 4 */ 5 6 package dm 7 8 import ( 9 "bytes" 10 "gitee.com/curryzheng/dm/util" 11 "math/rand" 12 "sync" 13 "time" 14 ) 15 16 /** 17 * dm_svc.conf中配置的服务名对应的一组实例, 以及相关属性和状态信息 18 * 19 * 需求: 20 * 1. 连接均匀分布在各个节点上 21 * 2. loginMode,loginStatus匹配 22 * 3. 连接异常节点比较耗时,在DB列表中包含异常节点时异常连接尽量靠后,减少对建连接速度的影响 23 * 24 * 25 * DB 连接顺序: 26 * 1. well distribution,每次连接都从列表的下一个节点开始 27 * 2. 用DB sort值按从大到小排序,sort为一个四位数XXXX,个位--serverStatus,十位--serverMode,共 有三种模式,最优先的 *100, 次优先的*10 28 */ 29 type epGroup struct { 30 name string 31 epList []*ep 32 props *Properties 33 epStartPos int32 // wellDistribute 起始位置 34 lock sync.Mutex 35 } 36 37 func newEPGroup(name string, serverList []*ep) *epGroup { 38 g := new(epGroup) 39 g.name = name 40 g.epList = serverList 41 if serverList == nil || len(serverList) == 0 { 42 g.epStartPos = -1 43 } else { 44 // 保证进程间均衡,起始位置采用随机值 45 g.epStartPos = rand.Int31n(int32(len(serverList))) - 1 46 } 47 return g 48 } 49 50 func (g *epGroup) connect(connector *DmConnector) (*DmConnection, error) { 51 var dbSelector = g.getEPSelector(connector) 52 var ex error = nil 53 // 如果配置了loginMode的主、备等优先策略,而未找到最高优先级的节点时持续循环switchtimes次,如果最终还是没有找到最高优先级则选择次优先级的 54 // 如果只有一个节点,一轮即可决定是否连接;多个节点时保证switchTimes轮尝试,最后一轮决定用哪个节点(由于节点已经按照模式优先级排序,最后一轮理论上就是连第一个节点) 55 var cycleCount int32 56 if len(g.epList) == 1 { 57 cycleCount = 1 58 } else { 59 cycleCount = connector.switchTimes + 1 60 } 61 for i := int32(0); i < cycleCount; i++ { 62 // 循环了一遍,如果没有符合要求的, 重新排序, 再尝试连接 63 conn, err := g.traverseServerList(connector, dbSelector, i == 0, i == cycleCount-1) 64 if err != nil { 65 ex = err 66 time.Sleep(time.Duration(connector.switchInterval) * time.Millisecond) 67 continue 68 } 69 return conn, nil 70 } 71 return nil, ex 72 } 73 74 func (g *epGroup) getEPSelector(connector *DmConnector) *epSelector { 75 if connector.epSelector == TYPE_HEAD_FIRST { 76 return newEPSelector(g.epList) 77 } else { 78 serverCount := int32(len(g.epList)) 79 sortEPs := make([]*ep, serverCount) 80 g.lock.Lock() 81 defer g.lock.Unlock() 82 g.epStartPos = (g.epStartPos + 1) % serverCount 83 for i := int32(0); i < serverCount; i++ { 84 sortEPs[i] = g.epList[(i+g.epStartPos)%serverCount] 85 } 86 return newEPSelector(sortEPs) 87 } 88 } 89 90 /** 91 * 从指定编号开始,遍历一遍服务名中的ip列表,只连接指定类型(主机或备机)的ip 92 * @param servers 93 * @param checkTime 94 * 95 * @exception 96 * DBError.ECJDBC_INVALID_SERVER_MODE 有站点的模式不匹配 97 * DBError.ECJDBC_COMMUNITION_ERROR 所有站点都连不上 98 */ 99 func (g *epGroup) traverseServerList(connector *DmConnector, epSelector *epSelector, first bool, last bool) (*DmConnection, error) { 100 epList := epSelector.sortDBList(first) 101 errorMsg := bytes.NewBufferString("") 102 var ex error = nil // 第一个错误 103 for _, server := range epList { 104 conn, err := server.connect(connector) 105 if err != nil { 106 if ex == nil { 107 ex = err 108 } 109 errorMsg.WriteString("[") 110 errorMsg.WriteString(server.String()) 111 errorMsg.WriteString("]") 112 errorMsg.WriteString(err.Error()) 113 errorMsg.WriteString(util.StringUtil.LineSeparator()) 114 continue 115 } 116 valid, err := epSelector.checkServerMode(conn, last) 117 if err != nil { 118 if ex == nil { 119 ex = err 120 } 121 errorMsg.WriteString("[") 122 errorMsg.WriteString(server.String()) 123 errorMsg.WriteString("]") 124 errorMsg.WriteString(err.Error()) 125 errorMsg.WriteString(util.StringUtil.LineSeparator()) 126 continue 127 } 128 if !valid { 129 conn.close() 130 err = ECGO_INVALID_SERVER_MODE.throw() 131 if ex == nil { 132 ex = err 133 } 134 errorMsg.WriteString("[") 135 errorMsg.WriteString(server.String()) 136 errorMsg.WriteString("]") 137 errorMsg.WriteString(err.Error()) 138 errorMsg.WriteString(util.StringUtil.LineSeparator()) 139 continue 140 } 141 return conn, nil 142 } 143 if ex != nil { 144 return nil, ex 145 } 146 return nil, ECGO_COMMUNITION_ERROR.addDetail(errorMsg.String()).throw() 147 }