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  }