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