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  }