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监听的端口、主服务器从服务器的连接地址信息。
<?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>
<proxy>
<property name="ipAddress">::</property>
<property name="port">7999</property>
<property name="backlog">1024</property>
</proxy>
</proxys>
<!-- 主服务器的连接信息。 -->
<master>
<property name="ipAddress">127.0.0.1</property>
<property name="port">1433</property>
<property name="backlog">256</property>
<!-- 权重,分0到10级。0级将那些能分发的请求都发送到从服务器。 -->
<property name="weight">5</property>
</master>
<!-- 从服务器的连接信息。 -->
<slaves>
<slave>
<property name="ipAddress">127.0.0.1</property>
<property name="port">1433</property>
<property name="backlog">256</property>
<!-- 必须提供正确的登陆user 和 password, 就采用该帐号和密码登录从服务器及心跳检测。-->
<property name="user">sa</property>
<property name="password">admin</property>
<!-- 权重,分0到10级。0级不会分发请求到从服务器。 -->
<property name="weight">5</property>
</slave>
</slaves>
<heartbeat>
<!-- 心跳SQL语句,检测从服务器是否在线。如果主服务器宕了之后,整个proxy服务不可用。 -->
<!-- 默认单位为秒,如果你以毫秒为单位,那么需要在后面跟ms单位,如1500ms即代表1.5s。 -->
<!-- 心跳SQL执行的间隔。 -->
<property name="keepalive">2</property>
<!-- 超过以下时间服务器仍然没响应,就认为宕机了。 -->
<property name="deadtime">30</property>
<property name="heartSQL">SELECT HOST_NAME();</property>
</heartbeat>
</akuma>
<akuma>\<proxys>下配置服务监听的端口,可以是IPV4或者IPV6的地址,还可包含多个监听地址。测试时,将测试程序的连接字符串修改为监听地址及端口就ok了。
<akuma>\<master>配置主服务器的IP地址及端口。该地址可为IPV4或IPV6地址,只能包含一个元素。权重必须位于0~10之间(包括0和10)。
<akuma>\<slaves>下配置从服务器的IP地址及端口。可包含任意个<slave>元素。
<akuma>\<heartbeat>从服务器心跳检测,心跳检测执行时采用的AppName为“SqlServerProxy Heartbeat”。
<?xml version="1.0"?>
<doc>
<assembly>
<name>Akuma.Contract</name>
</assembly>
<members>
<member name="T:Akuma.Contract.Heartbeat.HeartbeatInterval">
<summary>配置的心跳间隔参数。</summary>
</member>
<member name="P:Akuma.Contract.Heartbeat.HeartbeatInterval.KeepAlive">
<summary>心跳SQL执行的间隔,单位毫秒。</summary>
</member>
<member name="P:Akuma.Contract.Heartbeat.HeartbeatInterval.DeadTime">
<summary>超过以下时间服务器仍然没响应,就认为宕机了,单位毫秒。</summary>
</member>
<member name="P:Akuma.Contract.Heartbeat.HeartbeatInterval.HeartSql">
<summary>执行的心跳语句。</summary>
</member>
<member name="T:Akuma.Contract.Heartbeat.IHeartbeat">
<summary>心跳检测。</summary>
</member>
<member name="P:Akuma.Contract.Heartbeat.MonitorEntry.Conn">
<summary>心跳连接字符串</summary>
</member>
<member name="P:Akuma.Contract.Heartbeat.MonitorEntry.SlaveId">
<summary>心跳监视的服务器Id</summary>
</member>
<member name="P:Akuma.Contract.Heartbeat.MonitorEntry.Interval">
<summary>心跳监视的间隔、下线时间、心跳Sql。</summary>
</member>
<member name="T:Akuma.Contract.Heartbeat.OnlineChangedEventArgs">
<summary>标记监控项在线情况变化事件参数。</summary>
</member>
<member name="T:Akuma.Contract.Log.DefaultLogs">
<summary>
一个强类型的资源类,用于查找本地化的字符串等。
</summary>
</member>
<member name="P:Akuma.Contract.Log.DefaultLogs.ResourceManager">
<summary>
返回此类使用的缓存的 ResourceManager 实例。
</summary>
</member>
<member name="P:Akuma.Contract.Log.DefaultLogs.Culture">
<summary>
使用此强类型资源类,为所有资源查找
重写当前线程的 CurrentUICulture 属性。
</summary>
</member>
<member name="P:Akuma.Contract.Log.DefaultLogs.AcceptClientConnect">
<summary>
查找类似 {0} 连接到Proxy {1}。 的本地化字符串。
</summary>
</member>
<member name="P:Akuma.Contract.Log.DefaultLogs.ClientCorrectTerminated">
<summary>
查找类似 客户端 {0} 连接正常断开。 的本地化字符串。
</summary>
</member>
<member name="P:Akuma.Contract.Log.DefaultLogs.ClientProxyLoginSlaveSuccess">
<summary>
查找类似 客户端 {0} Proxy登陆到从服务器 {1} 成功。 的本地化字符串。
</summary>
</member>
<member name="P:Akuma.Contract.Log.DefaultLogs.ClientProxyMasterCorrectTerminated">
<summary>
查找类似 客户端 {0} Proxy所使用主服务器 {1} 连接正常断开。 的本地化字符串。
</summary>
</member>
<member name="P:Akuma.Contract.Log.DefaultLogs.ClientProxyMasterUnexpectedTerminated">
<summary>
查找类似 客户端 {0} Proxy所使用主服务器 {1} 连接意外中断。 的本地化字符串。
</summary>
</member>
<member name="P:Akuma.Contract.Log.DefaultLogs.ClientProxySlaveCorrectTerminated">
<summary>
查找类似 客户端 {0} Proxy所使用从服务器 {1} 连接正常断开。 的本地化字符串。
</summary>
</member>
<member name="P:Akuma.Contract.Log.DefaultLogs.ClientProxySlaveUnexpectedTerminated">
<summary>
查找类似 客户端 {0} Proxy所使用从服务器 {1} 连接意外中断。 的本地化字符串。
</summary>
</member>
<member name="P:Akuma.Contract.Log.DefaultLogs.ClientProxyTryConnectMasterFail">
<summary>
查找类似 客户端 {0} Proxy尝试连接到主服务器 {1} 失败。 的本地化字符串。
</summary>
</member>
<member name="P:Akuma.Contract.Log.DefaultLogs.ClientProxyTryLoginSlaveFail">
<summary>
查找类似 客户端 {0} Proxy尝试登陆到从服务器 {1} 失败。 的本地化字符串。
</summary>
</member>
<member name="P:Akuma.Contract.Log.DefaultLogs.ClientUnexpectedTerminated">
<summary>
查找类似 客户端 {0} 连接意外中断。 的本地化字符串。
</summary>
</member>
<member name="P:Akuma.Contract.Log.DefaultLogs.DbServersConfigFileNotExist">
<summary>
查找类似 在运行目录下没有发现配置文件“dbServers.xml”。 的本地化字符串。
</summary>
</member>
<member name="P:Akuma.Contract.Log.DefaultLogs.InternalCommand">
<summary>
查找类似 内置命令 的本地化字符串。
</summary>
</member>
<member name="P:Akuma.Contract.Log.DefaultLogs.MasterUseSlave">
<summary>
查找类似 主服务器会话 {0} 使用从服务器会话 {1}。 的本地化字符串。
</summary>
</member>
<member name="P:Akuma.Contract.Log.DefaultLogs.NotSupportClientEncryption">
<summary>
查找类似 客户端 {0} 连接字符串启用了加密,必须关闭数据库连接的加密选项才能正常使用。 的本地化字符串。
</summary>
</member>
<member name="P:Akuma.Contract.Log.DefaultLogs.NotSupportClientTdsVersion">
<summary>
查找类似 客户端 {0} 使用通信协议Tds4.2暂不支持,详情咨询开发商。 的本地化字符串。
</summary>
</member>
<member name="P:Akuma.Contract.Log.DefaultLogs.ProxyAppOutOfRestricitDate">
<summary>
查找类似 SQLServer Proxy已过期,不能继续使用。详情咨询projky@126.com或1781575586@qq.com。 的本地化字符串。
</summary>
</member>
<member name="P:Akuma.Contract.Log.DefaultLogs.RpcBatchLogFormat">
<summary>
查找类似 {0} 客户端 [{1}] {2} 事务 {3} 在 {4} 执行RPC [{5}] 个。 的本地化字符串。
</summary>
</member>
<member name="P:Akuma.Contract.Log.DefaultLogs.RpcLogFormat">
<summary>
查找类似 RPC {0} 名称 {1} 参数 {2} 个。 的本地化字符串。
</summary>
</member>
<member name="P:Akuma.Contract.Log.DefaultLogs.RpcParameterFormat">
<summary>
查找类似 参数 {0} 名称 {1} 类型 {2} 值 [{3}]。 的本地化字符串。
</summary>
</member>
<member name="P:Akuma.Contract.Log.DefaultLogs.RulePatternCannotBeEmpty">
<summary>
查找类似 "{0}" 规则表达式字符串不能为空。 的本地化字符串。
</summary>
</member>
<member name="P:Akuma.Contract.Log.DefaultLogs.SqlBatchLogFormat">
<summary>
查找类似 {0} 客户端 [{1}] {2} 事务 {3} 在 {4} 执行SQL [{5}]。 的本地化字符串。
</summary>
</member>
<member name="P:Akuma.Contract.Log.DefaultLogs.StartListenEndPoint">
<summary>
查找类似 开始监听 {0}。 的本地化字符串。
</summary>
</member>
<member name="P:Akuma.Contract.Log.ExecuteEndPoint.ClientRemoteEndPoint">
<summary>客户端连接到Proxy时使用的端点信息。</summary>
</member>
<member name="P:Akuma.Contract.Log.ExecuteEndPoint.ExecRemoteEndPoint">
<summary>客户端请求被执行的主服务器或从服务器端点信息。</summary>
</member>
<member name="P:Akuma.Contract.Log.ExecuteEndPoint.ClientAppName">
<summary>客户端请求被执行时使用的程序名称。</summary>
</member>
<member name="T:Akuma.Contract.Log.IBatchLog">
<summary>客户端完整请求执行的SQL和RPC记录。</summary>
</member>
<member name="T:Akuma.Contract.Log.IRunLog">
<summary>程序运行日志。</summary>
</member>
<member name="T:Akuma.Contract.Log.ISessionLog">
<summary>通信时字节日志记录。</summary>
</member>
<member name="T:Akuma.Contract.Policy.ApplayOption">
<summary>请求在从服务器上执行时的一些选项。</summary>
</member>
<member name="P:Akuma.Contract.Policy.ApplayOption.UseMasterSessionOption">
<summary>
默认为False。
True,在从服务器执行查询前,将主服务器的@@OPTIONS选项同步到从服务器;False,不同步。
仅同步ANSI_WARNINGS、ANSI_PADDING、ANSI_NULLS、ARITHABORT、ARITHIGNORE、QUOTED_IDENTIFIER 这几个选项。
当UseMasterSessionOption为True,且CustomOptions != SessionOptions.None时,会导致冲突,优先采用CustomOptions提供的选项。
</summary>
</member>
<member name="P:Akuma.Contract.Policy.ApplayOption.CustomOptions">
<summary>
默认为SessionOptions.None。
在从服务器执行查询前,将自定义的@@OPTIONS选项应用到从服务器。
当UseMasterSessionOption为True,且CustomOptions != SessionOptions.None时,会导致冲突,优先采用CustomOptions提供的选项。
</summary>
</member>
<member name="P:Akuma.Contract.Policy.ApplayOption.UseMaserSessionDbName">
<summary>
默认为True。
True,在从服务器执行查询前,将主服务器的数据库上下文名称同步到从服务器;False,不同步。
当UseMaserSessionDbName为True,且CustomDbName != null时,会导致冲突,优先采用CustomDbName提供的数据库名称。
UseMaserSessionDbName与CustomDbName必须至少一个有效。
</summary>
</member>
<member name="P:Akuma.Contract.Policy.ApplayOption.CustomDbName">
<summary>
默认null。
在从服务器执行查询前,将从服务器数据库上下文切换到自定义的数据库名称。
当UseMaserSessionDbName为True,且CustomDbName != null时,会导致冲突,优先采用CustomDbName提供的数据库名称。
UseMaserSessionDbName与CustomDbName必须至少一个有效。
</summary>
</member>
<member name="T:Akuma.Contract.Policy.ApplayResult">
<summary>
请求分发的结果。
</summary>
</member>
<member name="P:Akuma.Contract.Policy.ApplayResult.SlaveId">
<summary>从服务器标识Id。</summary>
</member>
<member name="P:Akuma.Contract.Policy.ApplayResult.Option">
<summary>在从服务器执行时的选项。</summary>
</member>
<member name="T:Akuma.Contract.Policy.ISlaveProvider">
<summary>每一个客户端连接必须使用不同的ISlaveProvider对象。否则,会造成负载不均衡。</summary>
</member>
<member name="M:Akuma.Contract.Policy.ISlaveProvider.ApplyRpc(Akuma.Contract.Policy.RequestRpcCollection,Akuma.Contract.Policy.SessionInfo,Akuma.Contract.Policy.SlaveInfo[])">
<summary>
请求一个Rpc批处理。
</summary>
<param name="rpcColl">Rpc批处理。</param>
<param name="sessionInfo">客户端会话的信息。</param>
<param name="slaves">从服务器列表。</param>
<returns>null,将请求分发到主服务器;非null值,根据从服务器Id将请求分发到对应从服务器。</returns>
</member>
<member name="M:Akuma.Contract.Policy.ISlaveProvider.ApplySql(Akuma.Contract.Policy.RequestSql,Akuma.Contract.Policy.SessionInfo,Akuma.Contract.Policy.SlaveInfo[])">
<summary>
请求一个Sql批处理。
</summary>
<param name="requestSql">Sql语句。</param>
<param name="sessionInfo">客户端会话的信息。</param>
<param name="slaves">从服务器列表。</param>
<returns>null,将请求分发到主服务器;非null值,根据从服务器Id将请求分发到对应从服务器。</returns>
</member>
<member name="T:Akuma.Contract.Policy.RequestRpcCollection">
<summary>
Rpc批处理请求对象。Rpc请求一般只包含一个rpc,但特别情况下会包含多个rpc。
</summary>
</member>
<member name="P:Akuma.Contract.Policy.RequestRpcCollection.UseTransaction">
<summary>
当前Rpc批处理请求是否在事务中。
</summary>
</member>
<member name="T:Akuma.Contract.Policy.RequestSql">
<summary>
Sql请求处理对象。
</summary>
</member>
<member name="P:Akuma.Contract.Policy.RequestSql.Text">
<summary>
请求执行的Sql语句。
</summary>
</member>
<member name="P:Akuma.Contract.Policy.RequestSql.UseTransaction">
<summary>
当前sql请求是否在事务中。
</summary>
</member>
<member name="T:Akuma.Contract.Policy.SessionInfo">
<summary>客户端连接到Proxy时的上下文信息。</summary>
</member>
<member name="P:Akuma.Contract.Policy.SessionInfo.AppName">
<summary>客户端连接时指定的ApplicationName。</summary>
</member>
<member name="P:Akuma.Contract.Policy.SessionInfo.InstanceName">
<summary>客户端连接时指定的实例名。</summary>
</member>
<member name="P:Akuma.Contract.Policy.SessionInfo.InitialCatalog">
<summary>客户端连接时指定的初始化数据库。</summary>
</member>
<member name="P:Akuma.Contract.Policy.SessionInfo.DbName">
<summary>客户端连接正在使用的上下文数据库。</summary>
</member>
<member name="P:Akuma.Contract.Policy.SessionInfo.IntegratedSecurity">
<summary>True,使用Windows身份认证;False,使用SQL Server认证。</summary>
</member>
<member name="P:Akuma.Contract.Policy.SessionInfo.LoginName">
<summary>SQL Server认证时使用的登录名。</summary>
</member>
<member name="P:Akuma.Contract.Policy.SessionInfo.HostName">
<summary>客户端计算机名称。</summary>
</member>
<member name="P:Akuma.Contract.Policy.SessionInfo.ClientRemoteEndPoint">
<summary>客户端连接时使用的地址信息。</summary>
</member>
<member name="P:Akuma.Contract.Policy.SessionInfo.SessionId">
<summary>Proxy 内部使用的会话Id。</summary>
</member>
<member name="P:Akuma.Contract.Policy.SessionInfo.MajorVersion">
<summary>客户端通过连接协议所获得的SQL SERVER 主版本号。</summary>
</member>
<member name="P:Akuma.Contract.Policy.SessionInfo.MinorVersion">
<summary>客户端通过连接协议所获得的SQL SERVER 次版本号。</summary>
</member>
<member name="P:Akuma.Contract.Policy.SessionInfo.BuildNum">
<summary>客户端通过连接协议所获得的SQL SERVER Build Number。</summary>
</member>
<member name="P:Akuma.Contract.Policy.SessionInfo.TdsVersion">
<summary>客户端连接所使用的协议版本。</summary>
</member>
<member name="P:Akuma.Contract.Policy.SessionInfo.MasterWeight">
<summary>主服务器权重。</summary>
</member>
<member name="T:Akuma.Contract.Policy.SlaveInfo">
<summary>从服务器配置的关键信息。</summary>
</member>
<member name="P:Akuma.Contract.Policy.SlaveInfo.Status">
<summary>On,从服务器当前在线;Off,从服务器当前宕机。</summary>
</member>
<member name="P:Akuma.Contract.Policy.SlaveInfo.Address">
<summary>从服务器的Ip地址信息。</summary>
</member>
<member name="P:Akuma.Contract.Policy.SlaveInfo.Port">
<summary>从服务器的端口信息。</summary>
</member>
<member name="P:Akuma.Contract.Policy.SlaveInfo.Weight">
<summary>从服务器进行负载的权重。</summary>
</member>
<member name="P:Akuma.Contract.Policy.SlaveInfo.SlaveId">
<summary>从服务器具有的唯一标识Id。</summary>
</member>
</members>
</doc>
该配置文件只会在启动时加载一次,修改后必须重启服务才能生效。
2、“balanceRules.xml” 配置当客户端的SQL请求或Rpc请求到来时,将依据本配置决定是否将请求分发到从服务器。
<?xml version="1.0" encoding="utf-8" ?>
<!-- 定义基于正则表达式的负载分发匹配规则 -->
<akuma>
<!--
匹配顺序[hostname] -> [clientendpoint] -> [appname] -> [loginname] -> [dbname] -> [statement]
匹配结果:
按匹配顺序,先应用reject规则,如果任何一条reject匹配成功,对应的请求将只发送到主服务器。
然后,同样按匹配顺序,再应用accept规则,如果任何一条accept匹配成功,对应的请求将发送到从服务器。
可同时并存任意条reject或accept规则。
-->
<!-- 针对无参数化查询语句和参数化查询生成的sql语句。 -->
<!--参数化查询生成的语句,是执行sp_executesql存储过程,此规则匹配存储过程参数“@statement”。-->
<sqlbatch>
<hostname>
<!--
ignorecase = true,正则表达式忽略大小写。
ignorecase = false,正则表达式大小写敏感。
transaction=true,请求在事务下,才进行匹配。
transaction=false,请求不在事务下,才进行匹配。
transaction=all,请求不论是否在事务下,都进行匹配。
如果不指定,默认ignorecase=true,transaction=all
正则表达式字符串不允许为空,为空将导致程序异常。可以删除reject 或者 accept项。
-->
<!--<reject ignorecase="true" transaction="all">Win-MustSendToMaster1</reject>
<reject ignorecase="true" transaction="all">Win-MustSendToMaster2</reject>
<accept ignorecase="true" transaction="all">Win-MustSendtoSlave1</accept>
<accept ignorecase="true" transaction="all">Win-MustSendtoSlave2</accept>-->
</hostname>
<!--如果在Ipv4下,一般要匹配的字符串格式为“192.168.201.1:2653” ip地址加端口号-->
<clientendpoint>
<!--<reject ignorecase="true" transaction="all">172.16.888.888</reject>
<accept ignorecase="true" transaction="all">172.16.999.999</accept>-->
</clientendpoint>
<appname>
<!-- Management studio 默认不发送到从服务器。 -->
<reject ignorecase="true" transaction="All">^Microsoft SQL Server Management Studio|^SQL Server Profiler</reject>
<!--<accept>.NET readonly client</accept>
<accept>reporting readonly client</accept>-->
</appname>
<loginname>
<!--<reject>updateableUser</reject>
<accept>readonlyUser</accept>-->
</loginname>
<dbname>
<!--<reject>updateableDbName</reject>
<accept>readonlyDbName</accept>-->
</dbname>
<statement>
<!-- 排除改变数据、修改会话变量等-->
<reject>insert|update|delete|begin|save|commit|rollback|create|alter|drop|truncate|set|#|##|exec|execute</reject>
<accept ignorecase="true" transaction="false">\bSELECT\b</accept>
</statement>
</sqlbatch>
<!--针对 Rpc 请求。-->
<rpcbatch>
<hostname>
<!--<reject ignorecase="true" transaction="all">Win-MustSendToMaster</reject>
<accept ignorecase="true" transaction="all">Win-MustSendtoSlave</accept>-->
</hostname>
<clientendpoint>
<!--<reject ignorecase="true" transaction="all">172.16.888.888</reject>
<accept ignorecase="true" transaction="all">172.16.999.999</accept>-->
</clientendpoint>
<appname>
<!-- Management studio 默认不发送到从服务器。 -->
<reject ignorecase="true" transaction="All">^Microsoft SQL Server Management Studio|^SQL Server Profiler</reject>
<!--<accept>.NET readonly client</accept>-->
<!--<accept>reporting readonly client</accept>-->
</appname>
<loginname>
<!--<reject>updateableUser</reject>
<accept>readonlyUser</accept>-->
</loginname>
<dbname>
<!--<reject>updateableDbName</reject>
<accept>readonlyDbName</accept>-->
</dbname>
<rpcname>
<reject>^usp\w+By(Delete|Update)$</reject>
<accept transaction="false" ignorecase="true">^uspGet\w+$|^usp\w+BySelect$</accept>
</rpcname>
</rpcbatch>
</akuma>
所有的规则内容都是正在表达式,不允许为空。可将事务中的查询也分发到从服务器,但这要业务需求允许才可以。
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>
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="log4net" type="System.Configuration.IgnoreSectionHandler" />
</configSections>
<!-- This section contains the log4net configuration settings -->
<log4net>
<appender name="BatchLogFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="${ALLUSERSPROFILE}\SqlServerProxy\sql_rpc_batch.log" />
<appendToFile value="true" />
<MaxSizeRollBackups value="0"/>
<maximumFileSize value="50MB" />
<!--<Threshold value="WARN"/>-->
<lockingModel type="log4net.Appender.FileAppender+MinimalLock"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%message%newline" />
</layout>
</appender>
<appender name="RunLogFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="${ALLUSERSPROFILE}\SqlServerProxy\apprun.log" />
<appendToFile value="true" />
<MaxSizeRollBackups value="0"/>
<!--<Threshold value="WARN"/>-->
<maximumFileSize value="50MB" />
<lockingModel type="log4net.Appender.FileAppender+MinimalLock"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%message%newline" />
</layout>
</appender>
<appender name="SessionLogFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="${ALLUSERSPROFILE}\SqlServerProxy\request_response.log" />
<appendToFile value="true" />
<MaxSizeRollBackups value="0"/>
<maximumFileSize value="500MB" />
<!--<Threshold value="WARN"/>-->
<lockingModel type="log4net.Appender.FileAppender+MinimalLock"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%message" />
</layout>
</appender>
<logger name="Akuma.Imp.IBatchLogDefault.BatchLog">
<level value="WARN" />
<appender-ref ref="BatchLogFileAppender" />
</logger>
<logger name="Akuma.Imp.IRunLogDefault.RunLog">
<level value="ALL" />
<appender-ref ref="RunLogFileAppender" />
</logger>
<logger name="Akuma.Imp.ISessionLogDefault.SessionLog">
<level value="WARN" />
<appender-ref ref="SessionLogFileAppender" />
</logger>
</log4net>
</configuration>
该配置文件修改后必须重启服务才能生效。
3、安装及3分钟快速体验
SqlServerProxy 环境要求:
.NET 4 Framework,.NET 3.5 Framework(两个同时装,现在支持Windows XP和Windows Server 2003了)。
安装:运行解压后的“InstallSvc.bat”,允许管理员权限即能成功安装,并自动启动SqlServerProxy服务。
卸载:运行解压后的“UninstallSvc.bat”,允许管理员权限即能成功卸载。
用vbs来进行自动化安装和配置
cacls
显示或修改任意访问控制列表 (ACL) 文件。
关于C盘整个盘符被锁定的解决办法
当使用这个命令 cacls c:\ /p administrator:N 后,你将失去对系统C盘控制权.这时,一般的方法
cacls c: /p administrator:F 命令恢复权限已经不可行了,可以用下面的一个方法尝试:
无getadmin.vbs这个文件
、
安装服务 installutil
set dir = %cd%
SET PATH = %PATH%,%dir% @echo off REM ________________________________________________________________ >nul 2>&1 "%SYSTEMROOT%\system32\cacls.exe" "%SYSTEMROOT%\system32\config\system" if '%errorlevel%' NEQ '0' ( echo 请求管理员权限... goto UACPrompt ) else ( goto gotAdmin ) :UACPrompt echo Set UAC = CreateObject^("Shell.Application"^) > "%temp%\getadmin.vbs" echo UAC.ShellExecute "%~s0", "", "", "runas", 1 >> "%temp%\getadmin.vbs" "%temp%\getadmin.vbs" exit /B :gotAdmin if exist "%temp%\getadmin.vbs" ( del "%temp%\getadmin.vbs" ) pushd "%CD%" CD /D "%~dp0" REM ________________________________________________________________ @echo 安装服务
%SystemRoot%\Microsoft.NET\Framework\v4.0.30319\installutil SqlServerProxy.exe @echo 启动服务
net start SqlServerProxy pause
3分钟快速体验:
#-----------#-----------------#------#------------------#--------------#
| |ip地址 |端口 | 机器名 |SQLServer版本 |
|-----------|-----------------|------|------------------|--------------|
|主服务器 |192.168.201.134 |1433 |WIN-D5FC592I20C |08R2 |
|-----------|-----------------|------|------------------|--------------|
|从服务器 |192.168.201.1 |1433 | AKUMA-PC |08R2 |
#-----------#-----------------#------#------------------#--------------#
从服务器登陆名及密码 cqUser cqPass@12ns
以上是测试环境,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">cqPass@12ns</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,请联系projky@126.com。
配置程序连接代理中间件字符串
假设你的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
比较核心的技术了 虚拟ip的一种实现方式(手工添加和C#添加)
需要删除一个虚拟IP的话,在string[] ip 中去掉虚拟IP和对应的子网掩码就ok了。经测试,和手工添加的效果一样,能ping通。
版权归博客园和作者共有。.NET类库源码参考 扣丁格鲁 www.projky.com,转载请注明出处。
f
虚拟IP技术在高可用领域像数据库SQLSERVER、web服务器等场景下使用很多,很疑惑它是怎么实现的,偶然,发现了一种方式可以实现虚拟ip。它的原理在于同一个物理网卡,是可以拥有多个ip地址的,至于虚拟网卡,也可用通过该方式拥有多个ip。下面,就拿安装vmware后的虚拟网卡做例子,该网络连接已具有192.168.32.1的ip,尝试添加一个192.168.32.6的虚拟ip地址,介绍这两种方式。
第一种,手工添加
1、打开控制面板,在想要添加虚拟ip的网络连接上右键,选择属性。在打开的属性窗口中选中“此连接使用下列项目”中的“Internet 协议版本 4 (TCP/IPv4)”。
2、点击“属性”按钮,进入“Internet 协议版本 4(TCP/IPv4)属性”窗口,可以看到,该网络连接设置了固定的ip地址为192.168.32.1。
3、点击“高级”按钮,进入“高级TCP/IP设置”窗口。
4、点击在IP地址栏中的添加按钮,弹出添加IP地址的窗口,输入192.168.32.6即可,子网掩码自动生成。
5、点击添加按钮后,一步一步点击确定按钮,最后,尝试ping一下该虚拟ip地址。能ping通,则成功了。
第二种, C#添加虚拟IP
采用编程方式,可以自动添加和移除该虚拟IP地址,所谓的虚拟IP地址的转移就是这么来实现的。当拥有虚拟IP的机器故障时,在故障机器上尝试删除该虚拟IP并在健康机器上重新添加该虚拟IP。具体的实现就相当复杂了,下面,就列出添加虚拟IP的代码,供参考。
需要在该项目上引用“System.Management”程序集。
using System;
using System.Management; namespace ProJKY{ public class AddVirtualIpAddress {
public void Test() {
ManagementClass findAdapters =
new ManagementClass("Win32_NetworkAdapterConfiguration");
ManagementObjectCollection adapters = findAdapters.GetInstances(); foreach (ManagementObject adapter in adapters) {
// 查找符合条件的网卡
if ((string)adapter["ServiceName"] != "VMware Virtual Ethernet Adapter for VMnet8")
continue; string[] ip = new string[] { "192.168.32.1","192.168.32.6"};
string[] mask = new string[] { "255.255.255.0","255.255.255.0"}; try{
ManagementBaseObject newIP = adapter.GetMethodParameters("EnableStatic"); newIP["IPAddress"] = ip;
newIP["SubnetMask"] = mask; ManagementBaseObject setIP = adapter.InvokeMethod("EnableStatic", newIP, null); UInt32 result = (UInt32)(setIP["returnValue"]);
}
catch (Exception) {
throw;
}
}
}
}
}
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 == ) {
accept = false;
break;
} string statement = rpc.Parameters[].Value as String;
if (String.IsNullOrEmpty(statement) == true) {
accept = false;
break;
} RequestSql requestSql = new RequestSql(rpc.Parameters[].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(, length); if (index < masterWeight && masterWeight > ) {
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 == ) {
return null;
}
int index = _random.Next(, 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();
}
}
优点
1、通过正则表达式来匹配
2、负载均衡
3、读写分离
4、会记录所有通过proxy 执行的sql到日志里
5、实时心跳检测从库是否宕机,如果宕机,命令不转发给从库
6、可以解释TDS协议数据包(最核心)
7、可能有自动摘除宕机的从库,没有测试过
TDS协议解释下载:http://files.cnblogs.com/files/lyhabc/%5BMS-TDS%5D--2.pdf
缺点
1、事务都在主库完成,不能跨越主从
2、只能统计C#发起的事务,其他语言不行,TSQL也不行
3、不包含自动故障转移,主服务器宕机,整个proxy服务不可用
4、只转发不理会两边数据是否一致,例如 :MySQL的binlog对比
5、主从数据库名称一定要相同
6、不支持加密连接
7、不支持分库分表
8、多字符集不支持
9、不支持黑白名单,它可以让我们自定义危险语句,比如delete忘了加where这样的SQL
10、统计的SQL长时间运行影响性能,并且阻止其运行,有统计,无阻止
11、无统一配置界面接口,直接修改XML文件来进行配置,XML配置文件比较分散
匹配顺序[hostname] -> [clientendpoint] -> [appname] -> [loginname] -> [dbname] -> [statement]
SQL匹配:insert|update|delete|begin|save|commit|rollback|create|alter|drop|truncate|set|#|##|exec|execute
源代码卖5万人民币
除了最核心的功能:可以解释TDS协议数据包 ,其他都是比较简单的功能,所以卖5万人民币不值得
测试的时候,SQL Server Proxy Service服务启动失败,测试无做好