SqlServerProxy的一些资料
下载地址:http://files.cnblogs.com/files/lyhabc/SqlServerProxy.rar
1、特性及实现原理
SqlServerProxy 特性介绍:
#-------#--------------------------------------------------------------------------------------#
|特性 |不需要版本升级就兼容SQL Server 2000 到 SQL Server 2014的所有版本。 |
|-------|--------------------------------------------------------------------------------------|
|特性2 |开箱即用:只需简单配置一下就能开始使用。 |
|-------|--------------------------------------------------------------------------------------|
|特性3 |内置命令能查看历史、实时、单个客户端的通信量、请求数、事务数等。 |
|-------|--------------------------------------------------------------------------------------|
|特性4 |开放请求分发处理接口,可基于业务需求自己定制,默认提供一个基于正则表达式匹配的实现。 |
#-------#--------------------------------------------------------------------------------------#
SqlServerProxy 实现原理
Proxy能解析SQLServer通信采用的TDS协议内容,因而能再次封装分发客户端的请求。但是,不负责数据同步,需要采用复制等技术来数据同步。没有分布式事务,只在通信层工作,不会往数据库中添加任何东西,还开放执行请求内容记录接口,方便对执行内容进行审计。
SqlServerProxy的其它说明:
不支持客户端连接时的加密选项为True,就是连接字符串中“Encrypt=True”,必须为False。
暂不支持使用TDS 4.2协议的客户端连接,TDS 4.2作为早期与Sybase兼容的通信协议,已很少使用。
使用MARS(多活动结果集)能正常连接到proxy,也能统计通信量信息,但暂不支持请求分发,将来会实现。
将请求分发到从服务器时,使用的上下文数据库是请求会话在主服务器上的上下文数据库名,如需不同名称,需要定制"ISlaveProvider"接口,详见“实践手册”。
最新版下载地址
http://www.projky.com/downloads/3a382709-d680-476c-853d-dc3af5c625de/SqlServerProxy.zip
最新版对.NET Framework版本要求降到了.NET 4,现在支持Windows XP和Windows Server 2003了,详见《安装及3分钟快速体验.txt》。
正则测试器
2、配置手册
1、“dbServers.xml” 配置SqlServerProxy监听的端口、主服务器从服务器的连接地址信息。
<akuma>\<proxys>下配置服务监听的端口,可以是IPV4或者IPV6的地址,还可包含多个监听地址。测试时,将测试程序的连接字符串修改为监听地址及端口就ok了。
<akuma>\<master>配置主服务器的IP地址及端口。该地址可为IPV4或IPV6地址,只能包含一个元素。权重必须位于0~10之间(包括0和10)。
<akuma>\<slaves>下配置从服务器的IP地址及端口。可包含任意个<slave>元素。
<akuma>\<heartbeat>从服务器心跳检测,心跳检测执行时采用的AppName为“SqlServerProxy Heartbeat”。
该配置文件只会在启动时加载一次,修改后必须重启服务才能生效。
2、“balanceRules.xml” 配置当客户端的SQL请求或Rpc请求到来时,将依据本配置决定是否将请求分发到从服务器。
所有的规则内容都是正在表达式,不允许为空。可将事务中的查询也分发到从服务器,但这要业务需求允许才可以。
reject规则:如果匹配成功,请求就会发送到主服务器。
accept规则:如果匹配成功,将按权重将请求发送到主服务器或者从服务器。
<akuma>\<sqlbatch>匹配无参的sql查询或参数化查询生成的语句(内部是执行sp_executesql存储过程,此规则匹配存储过程参数“@statement”)。-->
<akuma>\<rpcbatch>匹配的是Rpc请求。
特别提醒1:事务是不能够在主服务器开始再到从服务器结束的。必须尽量在主服务器开始和结束。对于“begin tran ..... commit tran”开始和结束都在一起的sql语句可以分发到从服务器,但对于先在主服务器执行“begin tran”再到从服务器执行“commit tran”的请求,客户端会报错。
特别提醒2:因为是采用正则表达式进行匹配,每个请求都要应用,所以配置的正则表达式必须注重效率,以免导致请求反应速度下降。
规则示例1:
<appname>
<reject transaction="All" ignorecase="true">^Microsoft SQL Server Management Studio|^SQL Server Profiler</reject>
</appname>
意味着无论是否在事务下(transaction="All"),忽略大小写(ignorecase="true"),凡是客户端应用程序名以"Microsoft SQL Server Management Studio"或"SQL Server Profiler"开头的请求都应该发送到主服务器。
规则示例2:
<statement>
<!-- 排除改变数据、修改会话变量等-->
<reject>insert|update|delete|begin|save|commit|rollback|create|alter|drop|truncate|set|#|##|exec|execute</reject>
<accept transaction="false" ignorecase="true">\bSELECT\b</accept>
</statement>
reject项意味着无论是否在事务下(默认的),忽略大小写(默认的),凡是包含关键字“insert|update|delete|begin|save|commit|rollback|create|alter|drop|truncate|set|#|##|exec|execute”的语句请求都应该发送到主服务器。
accept项意味着不在事务下(transaction="false"),忽略大小写,且包含“SELECT”单词的语句请求可分发到从服务器。
匹配顺序[hostname] -> [clientendpoint] -> [appname] -> [loginname] -> [dbname] -> [statement]
匹配结果: 按匹配顺序,先应用reject规则,如果任何一条reject匹配成功,对应的请求将只发送到主服务器。 然后,同样按匹配顺序,再应用accept规则,如果任何一条accept匹配成功,对应的请求将发送到从服务器。 可同时并存任意条reject或accept规则。
该配置文件修改后不必重启服务,而是实时生效。
3、 “SqlServerProxy.exe.config” 配置解析接口实现类的配置文件。SqlServerProxy.exe 服务启动时会读取该文件,如果解析失败,将不能正常使用服务。
IHeartbeat 接口即心跳检测接口,只检测从服务器。
IBatchLog 接口即请求执行记录接口,只记录Sql请求和Rpc请求,接口参数将包含执行sql的语句和rpc名称参数值等信息,可以单独实现该接口来审计通过SqlServerProxy的执行记录。
IRunLog 程序运行日志接口,包含客户端连接记录等。
ISessionLog 客户端和服务器响应时发送的网络包内容。
ISlaveProvider 接口即根据执行的Sql语句和rpc请求内容,选择从服务器的接口,决定读写分离,最为核心。默认提供了一个基于正则表达式的实现,但特定业务通过该接口定制,将能获得最大性能。
该配置文件修改后必须重启服务才能生效。
4、“Akuma.Imp.config” 配置默认的IBatchLog、IRunLog、ISessionLog记录的日志目录,和记录等级。
调整目录示例:
比如:<file value="${ALLUSERSPROFILE}\SqlServerProxy\apprun.log" /> 在本测试机上就是“C:\Users\All Users\SqlServerProxy\apprun.log”文件。需要将日志文件移动到“D:”下,就可以更改为:<file value="D:\Logs\SqlServerProxy\apprun.log" />
调整记录等级能让更多或更少记录被记下来。ISessionLog 和 IBatchLog 默认实现必须为“All”或“Debug”级别才记录,默认不记录。
调整记录等级示例:
<logger name="Akuma.Imp.IBatchLogDefault.BatchLog">
<level value="WARN" />
<appender-ref ref="BatchLogFileAppender" />
</logger>
更改为要记录请求日志内容:
<logger name="Akuma.Imp.IBatchLogDefault.BatchLog">
<level value="ALL" />
<appender-ref ref="BatchLogFileAppender" />
</logger>
该配置文件修改后必须重启服务才能生效。
3、安装及3分钟快速体验
SqlServerProxy 环境要求:
.NET 4 Framework,.NET 3.5 Framework(两个同时装,现在支持Windows XP和Windows Server 2003了)。
安装:运行解压后的“InstallSvc.bat”,允许管理员权限即能成功安装,并自动启动SqlServerProxy服务。
卸载:运行解压后的“UninstallSvc.bat”,允许管理员权限即能成功卸载。
3分钟快速体验:
#-----------#-----------------#------#------------------#--------------#
| |ip地址 |端口 | 机器名 |SQLServer版本 |
|-----------|-----------------|------|------------------|--------------|
|主服务器 |192.168.201.134 |1433 |WIN-D5FC592I20C |08R2 |
|-----------|-----------------|------|------------------|--------------|
|从服务器 |192.168.201.1 |1433 | AKUMA-PC |08R2 |
#-----------#-----------------#------#------------------#--------------#
从服务器登陆名及密码 cqUser [email protected]
以上是测试环境,SqlServerProxy运行在主服务器上。
第1步:编辑“dbServers.xml”,设置测试环境的值。仅列出关键部分。
#----------------------------------------------------------------------#
<proxys>
<proxy>
<!-- 使用“0.0.0.0”更方便 -->
<property name="ipAddress">0.0.0.0</property>
<property name="port">8999</property>
<property name="backlog">1024</property>
</proxy>
</proxys>
<master>
<property name="ipAddress">192.168.201.134</property>
<property name="port">1433</property>
<property name="backlog">256</property>
<!-- 主服务器权重设置为0,方便将符合条件的请求都分发到从服务器。-->
<property name="weight">0</property>
</master>
<slaves>
<slave>
<property name="ipAddress">192.168.201.1</property>
<property name="port">1433</property>
<property name="backlog">256</property>
<!-- 必须提供一个可用的账户。-->
<property name="user">cqUser</property>
<property name="password">[email protected]</property>
<property name="weight">5</property>
</slave>
</slaves>
#----------------------------------------------------------------------#
这样配置后,SqlServerProxy运行后将在主服务器上监听 8999 端口。
第2步:编辑"balanceRules.xml", 去掉对连接程序名“Microsoft SQL Server Management Studio”的限制。仅列出关键部分。
#----------------------------------------------------------------------#
<sqlbatch>
<appname>
<!-- 去掉对连接程序名“Microsoft SQL Server Management Studio”,默认配置文件只修改这一处。 -->
<reject ignorecase="true" transaction="All">^SQL Server Profiler</reject>
</appname>
<statement>
<reject>insert|update|delete|begin|save|commit|rollback|create|alter|drop|truncate|set|#|##|exec|execute</reject>
<accept ignorecase="true">\bSELECT\b</accept>
</statement>
</sqlbatch>
#----------------------------------------------------------------------#
第3步:重启SqlServerProxy服务(在“services.msc”名称显示为“SQL Server Proxy Service”),或者先运行“UninstallSvc.bat”再运行“InstallSvc.bat”重装。
第4步:使用“Management Studio”登陆到“192.168.201.134,8999”。
执行“SELECT @@SERVERNAME;”,可以看到返回结果为“AKUMA-PC”,没有reject规则匹配成功,而accept规则匹配成功了,是在从服务器上执行的。
执行“exec(‘SELECT @@SERVERNAME‘);”,因为包含了关键字“exec”,reject规则匹配成功,在主服务器执行,返回结果为“WIN-D5FC592I20C”。
执行“SELECT @@SERVERNAME; exec(‘SELECT @@SERVERNAME‘);” 同样因为包含关键字“exec”,reject规则匹配成功,在主服务器执行,返回结果为两个“WIN-D5FC592I20C”。
通过这个小测试,就能看到,通过简单的配置,能将请求分发到从服务器上去。
4、实践手册
目录:
1、内置命令
2、正则表达式测试器
3、怎么使用单机版的Proxy
4、虚拟ip结合的技巧
5、自定义SQL语句前缀来实现分发
6、业务需要,自己定制请求分发实现
1、内置命令
内置命令是仅连接到SqlServerProxy端口执行的特定sql语句,用来查看从服务器状态和会话历史等。命令是大小写敏感的,且固定大小,不能包含多余空格等,只能单次执行 一个命令。
命令1、“SELECT vt_active;” 查看当前活动连接信息。
返回结果解析:
#------------------#---------------------------------------#----------------------------------------------------#
|列名 |示例值 |客户端属性 |
|------------------|---------------------------------------|----------------------------------------------------|
|endpoint |192.168.201.1:34298 |连接时的远程ip及端口。 |
|------------------|---------------------------------------|----------------------------------------------------|
|app_name |Microsoft SQL Server Management Studio |应用程序名。 |
|------------------|---------------------------------------|----------------------------------------------------|
|to_client |2.55MB/5.23MB |2.55MB从服务器返回数据量,5.23MB返回客户端总数据量。|
|------------------|---------------------------------------|----------------------------------------------------|
|from_client |12.26KB/25.88KB |12.26KB到从服务器的数据量,25.88客户端发送总数据量。|
|------------------|---------------------------------------|----------------------------------------------------|
|sqlbatch_count |19 |执行SQL请求数。 |
|------------------|---------------------------------------|----------------------------------------------------|
|rpcbatch_count |1 |执行Rpc请求数。 |
|------------------|---------------------------------------|----------------------------------------------------|
|transaction_count |0 |执行事务数。 |
|------------------|---------------------------------------|----------------------------------------------------|
|command_count |6 |执行内置命令数。 |
|------------------|---------------------------------------|----------------------------------------------------|
|mars |False |True,启用Mars(多活动结果集);False,没启用。 |
|------------------|---------------------------------------|----------------------------------------------------|
|last_sql |select HOST_NAME(); |最后执行SQL(只包括最前面的256个字符)。 |
|------------------|---------------------------------------|----------------------------------------------------|
|last_rpc |UpdateTask |最后执行的Rpc名称。 |
#------------------#---------------------------------------#----------------------------------------------------#
特别说明:
to_client 前半部分代表了从服务器返回客户端的数据量,后半部分代表了从服务器+主服务器返回客户的数据量,两者的比值越大,从服务器分担的负载越多。
from_client 前半部分代表了客户端到从服务器的数据量,后半部分代表了客户端到从服务器+主服务器的数据量,两者的比值越大,从服务器分担的负载越多。
transaction_count 仅统计客户通过如"sqlConn.BeginTransaction();"开始的事务数,不统计如执行SQL“begin tran”引发的事务。
命令2、“SELECT vt_realtime;” 查看当前实时汇总连接信息。
返回结果解析:
#------------------#---------------------------------------#----------------------------------------------------#
|列名 |示例值 |SqlServerProxy在时间窗口内统计值 |
|------------------|---------------------------------------|----------------------------------------------------|
|span_time |21:46:58 - 21:47:08 |实时统计数据的时间窗口,为10秒。 |
|------------------|---------------------------------------|----------------------------------------------------|
|to_client |43.25MB/85.23MB |参见命令1。 |
|------------------|---------------------------------------|----------------------------------------------------|
|from_client |88.26KB/278.88KB |参见命令1。 |
|------------------|---------------------------------------|----------------------------------------------------|
|session_count |3 |新建立了3个会话。 |
|------------------|---------------------------------------|----------------------------------------------------|
|sqlbatch_count |58 |参见命令1。 |
|------------------|---------------------------------------|----------------------------------------------------|
|rpcbatch_count |20 |参见命令1。 |
|------------------|---------------------------------------|----------------------------------------------------|
|transaction_count |30 |参见命令1。 |
|------------------|---------------------------------------|----------------------------------------------------|
|command_count |3 |参见命令1。 |
#------------------#---------------------------------------#----------------------------------------------------#
命令3、“SELECT vt_history;” 查看当前SqlServerProxy历史运行统计信息。
返回结果解析:
#------------------#---------------------------------------#----------------------------------------------------#
|列名 |示例值 |SqlServerProxy启动后的历史统计 |
|------------------|---------------------------------------|----------------------------------------------------|
|run_time |0.12:37:10 |启动服务后连续运行了0天12小时37分10秒。 |
|------------------|---------------------------------------|----------------------------------------------------|
|to_client |70.58MB/138.84MB |参见命令1。 |
|------------------|---------------------------------------|----------------------------------------------------|
|from_client |24.15MB/54.67MB |参见命令1。 |
|------------------|---------------------------------------|----------------------------------------------------|
|session_count |92 |参见命令2。 |
|------------------|---------------------------------------|----------------------------------------------------|
|sqlbatch_count |2098 |参见命令1。 |
|------------------|---------------------------------------|----------------------------------------------------|
|rpcbatch_count |1673 |参见命令1。 |
|------------------|---------------------------------------|----------------------------------------------------|
|transaction_count |3200 |参见命令1。 |
|------------------|---------------------------------------|----------------------------------------------------|
|command_count |63 |参见命令1。 |
#------------------#---------------------------------------#----------------------------------------------------#
命令4、“SELECT vt_slave;” 查看SqlServerProxy配置的从服务器是否在线(即心跳检测的结果)。
返回结果解析:
#------------------#---------------------------------------#----------------------------------------------------#
|列名 |示例值 |SqlServerProxy配置的从服务器属性 |
|------------------|---------------------------------------|----------------------------------------------------|
|endpoint |192.168.201.1:1433 |从服务器的ip及端口。 |
|------------------|---------------------------------------|----------------------------------------------------|
|status |True |True,在线;False,宕机了。 |
#------------------#---------------------------------------#----------------------------------------------------#
命令5、“SELECT vt_server;” 查看运行SqlServerProxy服务的服务器信息
返回结果解析:
#------------------#---------------------------------------#----------------------------------------------------#
|列名 |示例值 |服务器信息 |
|------------------|---------------------------------------|----------------------------------------------------|
|date |2014-10-14 22:15:06.807 |服务器时间 |
|------------------|---------------------------------------|----------------------------------------------------|
|memory |2.86GB/4.00GB |共有4Gb内存,已使用2.86Gb。 |
|------------------|---------------------------------------|----------------------------------------------------|
|run_time |1.12:25:13 |运行时间。 |
|------------------|---------------------------------------|----------------------------------------------------|
|app_memory |64.88MB |SqlServerProxy使用内存量 |
#------------------#---------------------------------------#----------------------------------------------------#
2、正则表达式测试器
用来测试输入字符串对正则表达式是否匹配,以及将表达式在转义字符和正常字符间的转换。
Expression:在“Pattern”文本框中输正则表达式,在“Input”处输测试字符串,点击“Go”,出现“Yes”表达匹配,出现“No”表达不匹配。如果匹配成功,会计算执行匹配100次所用的毫秒数。
Escaping:在“Plain”文本框中输入要转义的正则表达式,自动在“Escaping”中出现转义后的字符串。反正也然。
3、怎么使用单机版的Proxy
使用单机可以统计SqlServerProxy所执行的会话信息、及对执行请求的审计、验证TDS解析效果等。
方法是在“dbServers.xml”中去掉所有“slave”元素即可。
<akuma>
<slaves>
<!-- 此处留空。 -->
</slaves>
</akuma>
4、虚拟ip结合的技巧
添加虚拟Ip的方法参见《手工添加虚拟IP的实现方法.pdf》或博客:http://www.cnblogs.com/ProJKY/p/VirtualIPAddressImp.html。
技巧1:在运行SqlServerProxy服务的服务器上添加虚拟Ip,客户端连接字符串中的DataSource就执行该虚拟Ip,方便切换SqlServerProxy服务的服务器。
技巧2:在“dbServers.xml”使用从服务器的虚拟Ip地址,也方便切换从服务器地址。压力小了,直接删除虚拟Ip地址即可。
技巧3:因为配置文件“dbServers.xml”只加载一次,所以后续所加从服务器不能够识别,但可以添加多个虚拟Ip,将来压力大了,再将这些虚拟Ip放到从服务器上,心跳检测能快速检测出使用虚拟Ip的从服务器上线了,而将请求分发到这些新上线的虚拟Ip从服务器上。
5、自定义SQL语句前缀来实现分发
使用该方法要求能修改客户端执行SQL请求的代码,但能避免正则表达式匹配速度慢的问题。
例如在执行只读可分发到从服务器执行的SQL语句前加前缀"--SqlServerProxyReadonly",对应“balanceRules.xml”里面添加一条accept规则“<accept>^--SqlServerProxyReadonly<accept>”能加快将只读查询分发到从服务器。类似的添加前缀如“--SqlServerProxyMaster”,对应“balanceRules.xml”里面添加一条reject规则“<reject>^--SqlServerProxyMaster<reject>”能加快将不符合条件的Sql语句分发到主服务器上。
6、业务需要,自己定制请求分发实现
请求分发在SqlServerProxy中被抽象为“ISlaveProvider”接口,位于“Akuma.Contract”程序集中。
实现“ISlaveProvider”接口的要点位于接口签名中,实现接口后,再修改“SqlServerProxy.exe.config”文件即可。
command.sql
SELECT vt_active; SELECT vt_realtime; SELECT vt_history; SELECT vt_slave; SELECT vt_server; SELECT N‘command‘ as Col
关于SqlServerProxy
本压缩包的程序将在2015.5.1日过期,过期后将不能继续使用。如需测试,请将数据库和运行Proxy服务的系统时间调到前面。
如果您觉得这些特性不够用或者Bug,请联系[email protected]。
配置程序连接代理中间件字符串
假设你的SqlServerProxy运行在192.168.2.136上,在dbServers.xml配置的代理监听如下:
<?xml version="1.0" encoding="utf-8" ?>
<!--定义主数据库和从服务器等信息。-->
<akuma>
<!-- 代理服务监听的端口信息,可以配置监听多个端口。 -->
<proxys>
<proxy>
<!-- 可为IPV6 或 IPV4 的地址。 -->
<property name="ipAddress">0.0.0.0</property>
<property name="port">8999</property>
<property name="backlog">1024</property>
</proxy>
那么根据配置,代理监听的端口号就是:8999,那么字符串中Data Source就是192.168.2.136,8999。
#-------------------------------------------------#-------------------------------------------------------------#
| 希望SqlServerProxy采用Windows认证连接主服务器 |Data Source=192.168.2.136,8999;Integrated Security = SSPI; |
| |Initial Catalog = myDataBase;Encrypt=False; |
|-------------------------------------------------|-------------------------------------------------------------|
| 希望SqlServerProxy采用SqlServer认证连接主服务器 |Data Source=192.168.2.136,8999;Initial Catalog = myDataBase; |
| |User ID = sa;Password=samplepassword;Encrypt=False; |
#-------------------------------------------------#-------------------------------------------------------------#
连接字符串里面的“Encrypt=False”可以不写,但不能为“Encrypt=True”,详情参见文档“1、特性及实现原理.txt”。
master_slave.png
手工添加虚拟IP的实现方法.pdf
f
f
f
f
f
f
f
ISlaveProvider.sample
using Akuma.Contract.Heartbeat; using Akuma.Contract.Policy; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; namespace CustomSlaveProvider { [Serializable] public class SlaveProvider : ISlaveProvider { public ApplayResult ApplyRpc(RequestRpcCollection rpcColl, SessionInfo sessionInfo, SlaveInfo[] slaves) { bool isSp_executesql = rpcColl.All(rpc => rpc.Name.Equals(SystemRpc.SP_EXECUTESQL, StringComparison.OrdinalIgnoreCase)); bool accept = false; if (isSp_executesql) { // 如果执行的是参数化查询 foreach (var rpc in rpcColl) { if (rpc.Parameters.Length == 0) { accept = false; break; } string statement = rpc.Parameters[0].Value as String; if (String.IsNullOrEmpty(statement) == true) { accept = false; break; } RequestSql requestSql = new RequestSql(rpc.Parameters[0].Value as String, rpcColl.UseTransaction); // _ruleService 是默认实现的正则表达式匹配服务 accept = _ruleService.AcceptSql(requestSql, sessionInfo); if (accept == false) { break; } } } else { accept = _ruleService.AcceptRpc(rpcColl, sessionInfo); } if (accept == false) { return null; // 返回null,将会分发到主服务器执行 } // 主服务器也有权重,按权重是否该在主服务器上执行 bool turnToMaster = SholdTurnToMaster(sessionInfo.MasterWeight, slaves); if (turnToMaster == true) { return null; } SlaveInfo currentSlave = slaves.FirstOrDefault(slave => _currentSlaveResult != null && slave.SlaveId == _currentSlaveResult.SlaveId && slave.Status == OnlineStatus.On); if (currentSlave != null) { // 如果可以到从服务器执行,那么尽量返回上一次分配的从服务器,避免不必要的网络流量。 return _currentSlaveResult; } _currentSlaveResult = GetRandomIndexByWeight(slaves); // 根据权重,计算该分配到那个从服务器 return _currentSlaveResult; } public ApplayResult ApplySql(RequestSql requestSql, SessionInfo sessionInfo, SlaveInfo[] slaves) { bool accept = _ruleService.AcceptSql(requestSql, sessionInfo); if (accept == false) { return null; } bool turnToMaster = SholdTurnToMaster(sessionInfo.MasterWeight, slaves); if (turnToMaster == true) { return null; } SlaveInfo currentSlave = slaves.FirstOrDefault(slave => _currentSlaveResult != null && slave.SlaveId == _currentSlaveResult.SlaveId && slave.Status == OnlineStatus.On); if (currentSlave != null) { return _currentSlaveResult; } _currentSlaveResult = GetRandomIndexByWeight(slaves); return _currentSlaveResult; } bool SholdTurnToMaster(int masterWeight, SlaveInfo[] slaves) { int length = masterWeight + slaves.Where(slave => slave.Status == OnlineStatus.On).Sum(slave => slave.Weight); int index = _random.Next(0, length); if (index < masterWeight && masterWeight > 0) { return true; } return false; } ApplayResult GetRandomIndexByWeight(SlaveInfo[] slaves) { SlaveInfo[] onlineSlaves = slaves.Where(slave => slave.Status == OnlineStatus.On).ToArray(); List<SlaveInfo> list = new List<SlaveInfo>(); foreach (var slave in onlineSlaves) { list.AddRange(Enumerable.Repeat(slave, slave.Weight)); } if (list.Count == 0) { return null; } int index = _random.Next(0, list.Count); string slaveId = list[index].SlaveId; ApplayResult applayResult = new ApplayResult(); applayResult.SlaveId = slaveId; return applayResult; } /// <summary> /// 当前会话的从服务器Id。 /// 每个SqlConnection连接就是一个会话,会话的从服务器确定后,就尽量不要修改,避免不必要的网络通信。 /// 如果修改了,会导致额外的网络通信。 /// </summary> ApplayResult _currentSlaveResult = null; Random _random = new Random(); } }